diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ea5f9a729c..8484a6f0af 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,3 +14,7 @@ updates: - dependency-name: "@stencil/sass" - dependency-name: "@stencil/vue-output-target" - dependency-name: "ionicons" + - dependency-name: "@capacitor/core" + - dependency-name: "@capacitor/keyboard" + - dependency-name: "@capacitor/haptics" + - dependency-name: "@capacitor/status-bar" diff --git a/CHANGELOG.md b/CHANGELOG.md index f25b4d1b95..d4a9ca08c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [7.1.4](https://github.com/ionic-team/ionic-framework/compare/v7.1.3...v7.1.4) (2023-07-19) + + +### Bug Fixes + +* **angular:** menu button is enabled with Angular Universal builds ([#27814](https://github.com/ionic-team/ionic-framework/issues/27814)) ([2cf1a0b](https://github.com/ionic-team/ionic-framework/commit/2cf1a0bcae7d766aa25951c53470876f9569906c)), closes [#27524](https://github.com/ionic-team/ionic-framework/issues/27524) [#22206](https://github.com/ionic-team/ionic-framework/issues/22206) +* **button:** submit form when pressing enter key ([#27790](https://github.com/ionic-team/ionic-framework/issues/27790)) ([b78af75](https://github.com/ionic-team/ionic-framework/commit/b78af7598f19ca5e1b440ddc0091a62d86321066)), closes [#19368](https://github.com/ionic-team/ionic-framework/issues/19368) +* **radio, checkbox, toggle:** add top and bottom margins when in ion-item ([#27788](https://github.com/ionic-team/ionic-framework/issues/27788)) ([35f0ec5](https://github.com/ionic-team/ionic-framework/commit/35f0ec581a55e0cb080f0793fb94d3e424c06d4d)), closes [#27498](https://github.com/ionic-team/ionic-framework/issues/27498) +* safari no longer adjusts text in landscape ([#27787](https://github.com/ionic-team/ionic-framework/issues/27787)) ([66584b0](https://github.com/ionic-team/ionic-framework/commit/66584b03d0b33507170f954009998c72fb3f7755)), closes [#27782](https://github.com/ionic-team/ionic-framework/issues/27782) +* **textarea:** stacked/floating textarea size is correct on safari ([#27766](https://github.com/ionic-team/ionic-framework/issues/27766)) ([2cb7013](https://github.com/ionic-team/ionic-framework/commit/2cb701395487c6a0304400f6b821659ae6def820)), closes [#27345](https://github.com/ionic-team/ionic-framework/issues/27345) + + + + + ## [7.1.3](https://github.com/ionic-team/ionic-framework/compare/v7.1.2...v7.1.3) (2023-07-12) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 278da208d8..4bf76784da 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [7.1.4](https://github.com/ionic-team/ionic-framework/compare/v7.1.3...v7.1.4) (2023-07-19) + + +### Bug Fixes + +* **angular:** menu button is enabled with Angular Universal builds ([#27814](https://github.com/ionic-team/ionic-framework/issues/27814)) ([2cf1a0b](https://github.com/ionic-team/ionic-framework/commit/2cf1a0bcae7d766aa25951c53470876f9569906c)), closes [#27524](https://github.com/ionic-team/ionic-framework/issues/27524) [#22206](https://github.com/ionic-team/ionic-framework/issues/22206) +* **button:** submit form when pressing enter key ([#27790](https://github.com/ionic-team/ionic-framework/issues/27790)) ([b78af75](https://github.com/ionic-team/ionic-framework/commit/b78af7598f19ca5e1b440ddc0091a62d86321066)), closes [#19368](https://github.com/ionic-team/ionic-framework/issues/19368) +* **radio, checkbox, toggle:** add top and bottom margins when in ion-item ([#27788](https://github.com/ionic-team/ionic-framework/issues/27788)) ([35f0ec5](https://github.com/ionic-team/ionic-framework/commit/35f0ec581a55e0cb080f0793fb94d3e424c06d4d)), closes [#27498](https://github.com/ionic-team/ionic-framework/issues/27498) +* safari no longer adjusts text in landscape ([#27787](https://github.com/ionic-team/ionic-framework/issues/27787)) ([66584b0](https://github.com/ionic-team/ionic-framework/commit/66584b03d0b33507170f954009998c72fb3f7755)), closes [#27782](https://github.com/ionic-team/ionic-framework/issues/27782) +* **textarea:** stacked/floating textarea size is correct on safari ([#27766](https://github.com/ionic-team/ionic-framework/issues/27766)) ([2cb7013](https://github.com/ionic-team/ionic-framework/commit/2cb701395487c6a0304400f6b821659ae6def820)), closes [#27345](https://github.com/ionic-team/ionic-framework/issues/27345) + + + + + ## [7.1.3](https://github.com/ionic-team/ionic-framework/compare/v7.1.2...v7.1.3) (2023-07-12) diff --git a/core/package-lock.json b/core/package-lock.json index cf2db537ae..b0150d9c76 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ionic/core", - "version": "7.1.3", + "version": "7.1.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@ionic/core", - "version": "7.1.3", + "version": "7.1.4", "license": "MIT", "dependencies": { "@stencil/core": "^3.4.0", @@ -15,10 +15,14 @@ }, "devDependencies": { "@axe-core/playwright": "^4.7.3", + "@capacitor/core": "^5.2.1", + "@capacitor/haptics": "^5.0.6", + "@capacitor/keyboard": "^5.0.6", + "@capacitor/status-bar": "^5.0.6", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "^2.0.0", "@jest/core": "^27.5.1", - "@playwright/test": "^1.36.0", + "@playwright/test": "^1.36.1", "@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-virtual": "^2.0.3", "@stencil/angular-output-target": "^0.7.1", @@ -602,6 +606,42 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@capacitor/core": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.1.tgz", + "integrity": "sha512-v7nzTQZj9l99Sp0v8C7Zq8QX6Cg5ljq7ASneWk/Hc5nBR5LOj/k3a+yEx/RoclWtkxJfs89Y5k+KJTFFQ6cLoA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/haptics": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-5.0.6.tgz", + "integrity": "sha512-UrMcR7p2X10ql4VLlowUuH/VckTeu0lj+RQpekxox14uxDmu5AGIFDK/iDTi8W6QZkxTJRZK6sbCjgwYgNJ7Pw==", + "dev": true, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/keyboard": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.6.tgz", + "integrity": "sha512-9GewAa/y2Hwkdw/Be8MTdiAjrFZ7TPDKpR44M0Y/0QMnK+mBbgzcoZ/UkuumWv6e2F1IAI+VY5eYVQHDeZcRoA==", + "dev": true, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/status-bar": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.6.tgz", + "integrity": "sha512-7od8CxsBnot1XMK3IeOkproFL4hgoKoWAc3pwUvmDOkQsXoxwQm4SR9mLwQavv1XfxtHbFV9Ukd7FwMxOPSViw==", + "dev": true, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -1501,13 +1541,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.0.tgz", - "integrity": "sha512-yN+fvMYtiyLFDCQos+lWzoX4XW3DNuaxjBu68G0lkgLgC6BP+m/iTxJQoSicz/x2G5EsrqlZTqTIP9sTgLQerg==", + "version": "1.36.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.1.tgz", + "integrity": "sha512-YK7yGWK0N3C2QInPU6iaf/L3N95dlGdbsezLya4n0ZCh3IL7VgPGxC6Gnznh9ApWdOmkJeleT2kMTcWPRZvzqg==", "dev": true, "dependencies": { "@types/node": "*", - "playwright-core": "1.36.0" + "playwright-core": "1.36.1" }, "bin": { "playwright": "cli.js" @@ -8150,9 +8190,9 @@ } }, "node_modules/playwright-core": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.0.tgz", - "integrity": "sha512-7RTr8P6YJPAqB+8j5ATGHqD6LvLLM39sYVNsslh78g8QeLcBs5750c6+msjrHUwwGt+kEbczBj1XB22WMwn+WA==", + "version": "1.36.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.1.tgz", + "integrity": "sha512-7+tmPuMcEW4xeCL9cp9KxmYpQYHKkyjwoXRnoeTowaeNat8PoBMk/HwCYhqkH2fRkshfKEOiVus/IhID2Pg8kg==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -10743,6 +10783,36 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@capacitor/core": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.1.tgz", + "integrity": "sha512-v7nzTQZj9l99Sp0v8C7Zq8QX6Cg5ljq7ASneWk/Hc5nBR5LOj/k3a+yEx/RoclWtkxJfs89Y5k+KJTFFQ6cLoA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "@capacitor/haptics": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-5.0.6.tgz", + "integrity": "sha512-UrMcR7p2X10ql4VLlowUuH/VckTeu0lj+RQpekxox14uxDmu5AGIFDK/iDTi8W6QZkxTJRZK6sbCjgwYgNJ7Pw==", + "dev": true, + "requires": {} + }, + "@capacitor/keyboard": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.6.tgz", + "integrity": "sha512-9GewAa/y2Hwkdw/Be8MTdiAjrFZ7TPDKpR44M0Y/0QMnK+mBbgzcoZ/UkuumWv6e2F1IAI+VY5eYVQHDeZcRoA==", + "dev": true, + "requires": {} + }, + "@capacitor/status-bar": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.6.tgz", + "integrity": "sha512-7od8CxsBnot1XMK3IeOkproFL4hgoKoWAc3pwUvmDOkQsXoxwQm4SR9mLwQavv1XfxtHbFV9Ukd7FwMxOPSViw==", + "dev": true, + "requires": {} + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -11381,14 +11451,14 @@ } }, "@playwright/test": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.0.tgz", - "integrity": "sha512-yN+fvMYtiyLFDCQos+lWzoX4XW3DNuaxjBu68G0lkgLgC6BP+m/iTxJQoSicz/x2G5EsrqlZTqTIP9sTgLQerg==", + "version": "1.36.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.36.1.tgz", + "integrity": "sha512-YK7yGWK0N3C2QInPU6iaf/L3N95dlGdbsezLya4n0ZCh3IL7VgPGxC6Gnznh9ApWdOmkJeleT2kMTcWPRZvzqg==", "dev": true, "requires": { "@types/node": "*", "fsevents": "2.3.2", - "playwright-core": "1.36.0" + "playwright-core": "1.36.1" } }, "@rollup/plugin-node-resolve": { @@ -16258,9 +16328,9 @@ } }, "playwright-core": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.0.tgz", - "integrity": "sha512-7RTr8P6YJPAqB+8j5ATGHqD6LvLLM39sYVNsslh78g8QeLcBs5750c6+msjrHUwwGt+kEbczBj1XB22WMwn+WA==", + "version": "1.36.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.1.tgz", + "integrity": "sha512-7+tmPuMcEW4xeCL9cp9KxmYpQYHKkyjwoXRnoeTowaeNat8PoBMk/HwCYhqkH2fRkshfKEOiVus/IhID2Pg8kg==", "dev": true }, "postcss": { diff --git a/core/package.json b/core/package.json index abc9dfa32e..6ae395d4a9 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/core", - "version": "7.1.3", + "version": "7.1.4", "description": "Base components for Ionic", "keywords": [ "ionic", @@ -37,10 +37,14 @@ }, "devDependencies": { "@axe-core/playwright": "^4.7.3", + "@capacitor/core": "^5.2.1", + "@capacitor/haptics": "^5.0.6", + "@capacitor/keyboard": "^5.0.6", + "@capacitor/status-bar": "^5.0.6", "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "^2.0.0", "@jest/core": "^27.5.1", - "@playwright/test": "^1.36.0", + "@playwright/test": "^1.36.1", "@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-virtual": "^2.0.3", "@stencil/angular-output-target": "^0.7.1", diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index a4e73d15f7..facb64f8d0 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -1,5 +1,5 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; -import { Component, Element, Event, Host, Prop, h } from '@stencil/core'; +import { Component, Element, Event, Host, Prop, Watch, h } from '@stencil/core'; import type { AnchorInterface, ButtonInterface } from '@utils/element-interface'; import type { Attributes } from '@utils/helpers'; import { inheritAriaAttributes, hasShadowDom } from '@utils/helpers'; @@ -32,6 +32,8 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf private inItem = false; private inListHeader = false; private inToolbar = false; + private formButtonEl: HTMLButtonElement | null = null; + private formEl: HTMLFormElement | null = null; private inheritedAttributes: Attributes = {}; @Element() el!: HTMLElement; @@ -52,6 +54,13 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * If `true`, the user cannot interact with the button. */ @Prop({ reflect: true }) disabled = false; + @Watch('disabled') + disabledChanged() { + const { disabled } = this; + if (this.formButtonEl) { + this.formButtonEl.disabled = disabled; + } + } /** * Set to `"block"` for a full-width button or to `"full"` for a full-width button @@ -144,6 +153,22 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf */ @Event() ionBlur!: EventEmitter; + connectedCallback(): void { + // Allow form to be submitted through `ion-button` + if (this.type !== 'button' && hasShadowDom(this.el)) { + this.formEl = this.findForm(); + if (this.formEl) { + // Create a hidden native button inside of the form + this.formButtonEl = document.createElement('button'); + this.formButtonEl.type = this.type; + this.formButtonEl.style.display = 'none'; + // Only submit if the button is not disabled. + this.formButtonEl.disabled = this.disabled; + this.formEl.appendChild(this.formButtonEl); + } + } + } + componentWillLoad() { this.inToolbar = !!this.el.closest('ion-buttons'); this.inListHeader = !!this.el.closest('ion-list-header'); @@ -177,12 +202,63 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf return form; } if (typeof form === 'string') { - const el = document.getElementById(form); - if (el instanceof HTMLFormElement) { - return el; + // Check if the string provided is a form id. + const el: HTMLElement | null = document.getElementById(form); + if (el) { + if (el instanceof HTMLFormElement) { + return el; + } else { + /** + * The developer specified a string for the form attribute, but the + * element with that id is not a form element. + */ + printIonWarning( + `Form with selector: "#${form}" could not be found. Verify that the id is attached to a
element.`, + this.el + ); + return null; + } + } else { + /** + * The developer specified a string for the form attribute, but the + * element with that id could not be found in the DOM. + */ + printIonWarning( + `Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`, + this.el + ); + return null; } } - return null; + if (form !== undefined) { + /** + * The developer specified a HTMLElement for the form attribute, + * but the element is not a HTMLFormElement. + * This will also catch if the developer tries to pass in null + * as the form attribute. + */ + printIonWarning( + `The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, + this.el + ); + return null; + } + /** + * If the form element is not set, the button may be inside + * of a form element. Query the closest form element to the button. + */ + return this.el.closest('form'); + } + + private submitForm(ev: Event) { + // this button wants to specifically submit a form + // climb up the dom to see if we're in a + // and if so, then use JS to submit it + if (this.formEl && this.formButtonEl) { + ev.preventDefault(); + + this.formButtonEl.click(); + } } private handleClick = (ev: Event) => { @@ -190,49 +266,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf if (this.type === 'button') { openURL(this.href, ev, this.routerDirection, this.routerAnimation); } else if (hasShadowDom(el)) { - // this button wants to specifically submit a form - // climb up the dom to see if we're in a - // and if so, then use JS to submit it - let formEl = this.findForm(); - const { form } = this; - - if (!formEl && form !== undefined) { - /** - * The developer specified a form selector for - * the button to submit, but it was not found. - */ - if (typeof form === 'string') { - printIonWarning( - `Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`, - el - ); - } else { - printIonWarning( - `The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, - el - ); - } - return; - } - - if (!formEl) { - /** - * If the form element is not set, the button may be inside - * of a form element. Query the closest form element to the button. - */ - formEl = el.closest('form'); - } - - if (formEl) { - ev.preventDefault(); - - const fakeButton = document.createElement('button'); - fakeButton.type = this.type; - fakeButton.style.display = 'none'; - formEl.appendChild(fakeButton); - fakeButton.click(); - fakeButton.remove(); - } + this.submitForm(ev); } }; diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index dacd22601b..69e525be4b 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -44,7 +44,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => await page.setContent( ` - Submit + Submit
`, config @@ -56,19 +56,93 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); + + test('should submit the closest form by pressing the `enter` button on a form element', async ({ + page, + }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/19368', + }); + + await page.setContent( + ` +
+ + Submit +
+ `, + config + ); + + const submitEvent = await page.spyOnEvent('submit'); + + await page.press('input', 'Enter'); + + expect(submitEvent).toHaveReceivedEvent(); + }); + + test('should submit the closest form with multiple elements by pressing the `enter` button', async ({ + page, + }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/19368', + }); + + await page.setContent( + ` +
+ + + Submit +
+ `, + config + ); + + const submitEvent = await page.spyOnEvent('submit'); + + await page.press('input', 'Enter'); + + expect(submitEvent).toHaveReceivedEvent(); + }); + + test('should not submit the closest form when button is disabled', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/19368', + }); + + await page.setContent( + ` +
+ + Submit +
+ `, + config + ); + + const submitEvent = await page.spyOnEvent('submit'); + + await page.press('input', 'Enter'); + + expect(submitEvent).not.toHaveReceivedEvent(); + }); }); test.describe(title('should throw a warning if the form cannot be found'), () => { test('form is a string selector', async ({ page }) => { - await page.setContent(`Submit`, config); - const logs: string[] = []; page.on('console', (msg) => { - logs.push(msg.text()); + if (msg.type() === 'warning') { + logs.push(msg.text()); + } }); - await page.click('ion-button'); + await page.setContent(`Submit`, config); expect(logs.length).toBe(1); expect(logs[0]).toContain( @@ -77,6 +151,14 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => }); test('form is an element reference', async ({ page }) => { + const logs: string[] = []; + + page.on('console', (msg) => { + if (msg.type() === 'warning') { + logs.push(msg.text()); + } + }); + await page.setContent( ` Submit @@ -90,14 +172,6 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => config ); - const logs: string[] = []; - - page.on('console', (msg) => { - logs.push(msg.text()); - }); - - await page.click('ion-button'); - expect(logs.length).toBe(1); expect(logs[0]).toContain( '[Ionic Warning]: The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.' diff --git a/core/src/components/menu/menu.tsx b/core/src/components/menu/menu.tsx index 56c6e3a919..c010e72d2d 100644 --- a/core/src/components/menu/menu.tsx +++ b/core/src/components/menu/menu.tsx @@ -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'; @@ -189,7 +190,6 @@ export class Menu implements ComponentInterface, MenuI { } if (!Build.isBrowser) { - this.disabled = true; return; } @@ -705,9 +705,18 @@ export class Menu implements ComponentInterface, MenuI { this.forceClosing(); } - if (!this.disabled) { - menuController._setActiveMenu(this); + 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'); } diff --git a/core/src/components/modal/utils.ts b/core/src/components/modal/utils.ts index 90129839ae..e2ea7e0142 100644 --- a/core/src/components/modal/utils.ts +++ b/core/src/components/modal/utils.ts @@ -74,6 +74,7 @@ export const getBackdropValueForSheet = (x: number, backdropBreakpoint: number) * support for Style.Default. */ export const setCardStatusBarDark = () => { + // TODO FW-4696 Remove supportDefaultStatusBarStyle in Ionic v8 if (!win || win.innerWidth >= 768 || !StatusBar.supportsDefaultStatusBarStyle()) { return; } @@ -82,6 +83,7 @@ export const setCardStatusBarDark = () => { }; export const setCardStatusBarDefault = (defaultStyle = Style.Default) => { + // TODO FW-4696 Remove supportDefaultStatusBarStyle in Ionic v8 if (!win || win.innerWidth >= 768 || !StatusBar.supportsDefaultStatusBarStyle()) { return; } diff --git a/core/src/components/refresher/refresher.tsx b/core/src/components/refresher/refresher.tsx index 858f5c394d..f762ffe7ac 100644 --- a/core/src/components/refresher/refresher.tsx +++ b/core/src/components/refresher/refresher.tsx @@ -8,7 +8,7 @@ import { printIonContentErrorMsg, } from '@utils/content'; import { clamp, componentOnReady, getElementRoot, raf, transitionEndAsync } from '@utils/helpers'; -import { hapticImpact } from '@utils/native/haptic'; +import { ImpactStyle, hapticImpact } from '@utils/native/haptic'; import { getIonMode } from '../../global/ionic-global'; import type { Animation, Gesture, GestureDetail } from '../../interface'; @@ -246,7 +246,7 @@ export class Refresher implements ComponentInterface { if (!this.didRefresh) { this.beginRefresh(); this.didRefresh = true; - hapticImpact({ style: 'light' }); + hapticImpact({ style: ImpactStyle.Light }); /** * Translate the content element otherwise when pointer is removed diff --git a/core/src/components/toggle/test/item/label.e2e.ts b/core/src/components/toggle/test/item/toggle.e2e.ts similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts rename to core/src/components/toggle/test/item/toggle.e2e.ts diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-ios-rtl-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-inset-list-md-rtl-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-ios-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-item-color-md-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-ios-rtl-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-list-md-rtl-Mobile-Safari-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Chrome-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Chrome-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Chrome-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Chrome-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Firefox-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Firefox-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Firefox-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Firefox-linux.png diff --git a/core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Safari-linux.png b/core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Safari-linux.png similarity index 100% rename from core/src/components/toggle/test/item/label.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Safari-linux.png rename to core/src/components/toggle/test/item/toggle.e2e.ts-snapshots/toggle-long-label-in-item-md-ltr-Mobile-Safari-linux.png diff --git a/core/src/css/structure.scss b/core/src/css/structure.scss index 75bed7817c..1e39d0296e 100644 --- a/core/src/css/structure.scss +++ b/core/src/css/structure.scss @@ -17,6 +17,7 @@ html { width: 100%; height: 100%; + -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } @@ -80,6 +81,7 @@ body { word-wrap: break-word; overscroll-behavior-y: none; + -webkit-text-size-adjust: none; text-size-adjust: none; } diff --git a/core/src/utils/input-shims/hacks/scroll-assist.ts b/core/src/utils/input-shims/hacks/scroll-assist.ts index 6de8832f99..97928d608e 100644 --- a/core/src/utils/input-shims/hacks/scroll-assist.ts +++ b/core/src/utils/input-shims/hacks/scroll-assist.ts @@ -1,6 +1,7 @@ +import type { KeyboardResizeOptions } from '@capacitor/keyboard'; + import { getScrollElement, scrollByPoint } from '../../content'; import { raf } from '../../helpers'; -import type { KeyboardResizeOptions } from '../../native/keyboard'; import { KeyboardResize } from '../../native/keyboard'; import { relocateInput, SCROLL_AMOUNT_PADDING } from './common'; diff --git a/core/src/utils/keyboard/keyboard-controller.ts b/core/src/utils/keyboard/keyboard-controller.ts index a0e2a7c230..2cf0c19d34 100644 --- a/core/src/utils/keyboard/keyboard-controller.ts +++ b/core/src/utils/keyboard/keyboard-controller.ts @@ -1,6 +1,6 @@ import { doc, win } from '@utils/browser'; -import { KeyboardResize, Keyboard } from '../native/keyboard'; +import { Keyboard, KeyboardResize } from '../native/keyboard'; /** * The element that resizes when the keyboard opens diff --git a/core/src/utils/native/capacitor.ts b/core/src/utils/native/capacitor.ts new file mode 100644 index 0000000000..5554f28f6e --- /dev/null +++ b/core/src/utils/native/capacitor.ts @@ -0,0 +1,9 @@ +import type { CapacitorGlobal } from '@capacitor/core'; +import { win } from '@utils/browser'; + +export const getCapacitor = () => { + if (win !== undefined) { + return (win as any).Capacitor as CapacitorGlobal; + } + return undefined; +}; diff --git a/core/src/utils/native/haptic.ts b/core/src/utils/native/haptic.ts index d56d442c39..cbcc909586 100644 --- a/core/src/utils/native/haptic.ts +++ b/core/src/utils/native/haptic.ts @@ -1,24 +1,91 @@ -// Main types for this API +import type { + HapticsPlugin, + NotificationType as CapacitorNotificationType, + ImpactStyle as CapacitorImpactStyle, +} from '@capacitor/haptics'; + +import { getCapacitor } from './capacitor'; + +export enum ImpactStyle { + /** + * A collision between large, heavy user interface elements + * + * @since 1.0.0 + */ + Heavy = 'HEAVY', + /** + * A collision between moderately sized user interface elements + * + * @since 1.0.0 + */ + Medium = 'MEDIUM', + /** + * A collision between small, light user interface elements + * + * @since 1.0.0 + */ + Light = 'LIGHT', +} + interface HapticImpactOptions { - style: 'light' | 'medium' | 'heavy'; + style: CapacitorImpactStyle; +} + +export enum NotificationType { + /** + * A notification feedback type indicating that a task has completed successfully + * + * @since 1.0.0 + */ + Success = 'SUCCESS', + /** + * A notification feedback type indicating that a task has produced a warning + * + * @since 1.0.0 + */ + Warning = 'WARNING', + /** + * A notification feedback type indicating that a task has failed + * + * @since 1.0.0 + */ + Error = 'ERROR', } interface HapticNotificationOptions { - style: 'success' | 'warning' | 'error'; + type: CapacitorNotificationType; +} + +interface TapticEngine { + gestureSelectionStart: () => void; + gestureSelectionChanged: () => void; + gestureSelectionEnd: () => void; } const HapticEngine = { - getEngine() { - const win = window as any; - return win.TapticEngine || (win.Capacitor?.isPluginAvailable('Haptics') && win.Capacitor.Plugins.Haptics); + getEngine(): HapticsPlugin | undefined { + const tapticEngine = (window as any).TapticEngine; + if (tapticEngine) { + // Cordova + // TODO FW-4707 - Remove this in Ionic 8 + return tapticEngine; + } + const capacitor = getCapacitor(); + + if (capacitor?.isPluginAvailable('Haptics')) { + // Capacitor + return capacitor.Plugins.Haptics as HapticsPlugin; + } + return undefined; }, available() { - const win = window as any; const engine = this.getEngine(); if (!engine) { return false; } + const capacitor = getCapacitor(); + /** * Developers can manually import the * Haptics plugin in their app which will cause @@ -28,25 +95,30 @@ const HapticEngine = { * the Vibrate API. This check avoids that error * if the browser does not support the Vibrate API. */ - if (win.Capacitor?.getPlatform() === 'web') { + if (capacitor?.getPlatform() === 'web') { return typeof navigator !== 'undefined' && navigator.vibrate !== undefined; } return true; }, isCordova() { - return !!(window as any).TapticEngine; + return (window as any).TapticEngine !== undefined; }, isCapacitor() { - const win = window as any; - return !!win.Capacitor; + return getCapacitor() !== undefined; }, impact(options: HapticImpactOptions) { const engine = this.getEngine(); if (!engine) { return; } - const style = this.isCapacitor() ? options.style.toUpperCase() : options.style; + /** + * To provide backwards compatibility with Cordova apps, + * we convert the style to lowercase. + * + * TODO: FW-4707 - Remove this in Ionic 8 + */ + const style = this.isCapacitor() ? options.style : (options.style.toLowerCase() as ImpactStyle); engine.impact({ style }); }, notification(options: HapticNotificationOptions) { @@ -54,11 +126,24 @@ const HapticEngine = { if (!engine) { return; } - const style = this.isCapacitor() ? options.style.toUpperCase() : options.style; - engine.notification({ style }); + /** + * To provide backwards compatibility with Cordova apps, + * we convert the style to lowercase. + * + * TODO: FW-4707 - Remove this in Ionic 8 + */ + const type = this.isCapacitor() ? options.type : (options.type.toLowerCase() as NotificationType); + engine.notification({ type }); }, selection() { - this.impact({ style: 'light' }); + /** + * To provide backwards compatibility with Cordova apps, + * we convert the style to lowercase. + * + * TODO: FW-4707 - Remove this in Ionic 8 + */ + const style = this.isCapacitor() ? ImpactStyle.Light : ('light' as ImpactStyle); + this.impact({ style }); }, selectionStart() { const engine = this.getEngine(); @@ -68,7 +153,7 @@ const HapticEngine = { if (this.isCapacitor()) { engine.selectionStart(); } else { - engine.gestureSelectionStart(); + (engine as unknown as TapticEngine).gestureSelectionStart(); } }, selectionChanged() { @@ -79,7 +164,7 @@ const HapticEngine = { if (this.isCapacitor()) { engine.selectionChanged(); } else { - engine.gestureSelectionChanged(); + (engine as unknown as TapticEngine).gestureSelectionChanged(); } }, selectionEnd() { @@ -90,7 +175,7 @@ const HapticEngine = { if (this.isCapacitor()) { engine.selectionEnd(); } else { - engine.gestureSelectionEnd(); + (engine as unknown as TapticEngine).gestureSelectionEnd(); } }, }; @@ -135,7 +220,7 @@ export const hapticSelectionEnd = () => { /** * Use this to indicate success/failure/warning to the user. - * options should be of the type `{ type: 'success' }` (or `warning`/`error`) + * options should be of the type `{ type: NotificationType.SUCCESS }` (or `WARNING`/`ERROR`) */ export const hapticNotification = (options: HapticNotificationOptions) => { hapticAvailable() && HapticEngine.notification(options); @@ -143,7 +228,7 @@ export const hapticNotification = (options: HapticNotificationOptions) => { /** * Use this to indicate success/failure/warning to the user. - * options should be of the type `{ style: 'light' }` (or `medium`/`heavy`) + * options should be of the type `{ style: ImpactStyle.LIGHT }` (or `MEDIUM`/`HEAVY`) */ export const hapticImpact = (options: HapticImpactOptions) => { hapticAvailable() && HapticEngine.impact(options); diff --git a/core/src/utils/native/keyboard.ts b/core/src/utils/native/keyboard.ts index 6a4e88efbe..cefcd6a64b 100644 --- a/core/src/utils/native/keyboard.ts +++ b/core/src/utils/native/keyboard.ts @@ -1,30 +1,55 @@ -import { win } from '../browser'; +import type { CapacitorException } from '@capacitor/core'; +import type { KeyboardPlugin, KeyboardResizeOptions } from '@capacitor/keyboard'; -import type { NativePluginError } from './native-interface'; - -// Interfaces source: https://capacitorjs.com/docs/apis/keyboard#interfaces -export interface KeyboardResizeOptions { - mode: KeyboardResize; -} +import { getCapacitor } from './capacitor'; +import { ExceptionCode } from './native-interface'; export enum KeyboardResize { + /** + * Only the `body` HTML element will be resized. + * Relative units are not affected, because the viewport does not change. + * + * @since 1.0.0 + */ Body = 'body', + /** + * Only the `ion-app` HTML element will be resized. + * Use it only for Ionic Framework apps. + * + * @since 1.0.0 + */ Ionic = 'ionic', + /** + * The whole native Web View will be resized when the keyboard shows/hides. + * This affects the `vh` relative unit. + * + * @since 1.0.0 + */ Native = 'native', + /** + * Neither the app nor the Web View are resized. + * + * @since 1.0.0 + */ None = 'none', } export const Keyboard = { - getEngine() { - return (win as any)?.Capacitor?.isPluginAvailable('Keyboard') && (win as any)?.Capacitor.Plugins.Keyboard; + getEngine(): KeyboardPlugin | undefined { + const capacitor = getCapacitor(); + + if (capacitor?.isPluginAvailable('Keyboard')) { + return capacitor.Plugins.Keyboard as KeyboardPlugin; + } + return undefined; }, getResizeMode(): Promise { const engine = this.getEngine(); if (!engine?.getResizeMode) { return Promise.resolve(undefined); } - return engine.getResizeMode().catch((e: NativePluginError) => { - if (e.code === 'UNIMPLEMENTED') { + return engine.getResizeMode().catch((e: CapacitorException) => { + if (e.code === ExceptionCode.Unimplemented) { // If the native implementation is not available // we treat it the same as if the plugin is not available. return undefined; diff --git a/core/src/utils/native/native-interface.ts b/core/src/utils/native/native-interface.ts index a486f455aa..f43c00e4ae 100644 --- a/core/src/utils/native/native-interface.ts +++ b/core/src/utils/native/native-interface.ts @@ -1,13 +1,17 @@ -/** - * Used to represent a generic error from a native plugin call. - */ -export interface NativePluginError { +export enum ExceptionCode { /** - * The error code. + * API is not implemented. + * + * This usually means the API can't be used because it is not implemented for + * the current platform. */ - code?: string; + Unimplemented = 'UNIMPLEMENTED', /** - * The error message. + * API is not available. + * + * This means the API can't be used right now because: + * - it is currently missing a prerequisite, such as network connectivity + * - it requires a particular platform or browser version */ - message?: string; + Unavailable = 'UNAVAILABLE', } diff --git a/core/src/utils/native/status-bar.ts b/core/src/utils/native/status-bar.ts index f57a15c5be..8d41d0264e 100644 --- a/core/src/utils/native/status-bar.ts +++ b/core/src/utils/native/status-bar.ts @@ -1,7 +1,9 @@ -import { win } from '../browser'; +import type { StatusBarPlugin, Style as StatusBarStyle } from '@capacitor/status-bar'; + +import { getCapacitor } from './capacitor'; interface StyleOptions { - style: Style; + style: StatusBarStyle; } export enum Style { @@ -11,17 +13,24 @@ export enum Style { } export const StatusBar = { - getEngine() { - return (win as any)?.Capacitor?.isPluginAvailable('StatusBar') && (win as any)?.Capacitor.Plugins.StatusBar; + getEngine(): StatusBarPlugin | undefined { + const capacitor = getCapacitor(); + + if (capacitor?.isPluginAvailable('StatusBar')) { + return capacitor.Plugins.StatusBar as StatusBarPlugin; + } + return undefined; }, + // TODO FW-4696 Remove supportDefaultStatusBarStyle in Ionic v8 supportsDefaultStatusBarStyle() { + const capacitor = getCapacitor() as any; /** * The 'DEFAULT' status bar style was added * to the @capacitor/status-bar plugin in Capacitor 3. * PluginHeaders is only supported in Capacitor 3+, * so we can use this to detect Capacitor 3. */ - return !!(win as any)?.Capacitor?.PluginHeaders; + return !!capacitor?.PluginHeaders; }, setStyle(options: StyleOptions) { const engine = this.getEngine(); @@ -31,7 +40,7 @@ export const StatusBar = { engine.setStyle(options); }, - getStyle: async function (): Promise