From 11554a5d3590c660dbf609931dcb63cc2daf79cb Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Tue, 11 Mar 2025 13:39:31 -0700 Subject: [PATCH] fix(vue): update output target and properly emit events (#30227) Issue number: resolves #30206 resolves #30178 resolves #30177 resolves #30175 resolves #30170 --------- ## What is the current behavior? There have been plenty of issues reported in regards to Vue components failing to propagate events. It seems like when we updated the Vue output target and started to use the provided runtime code from the output target, we have changed the way how event names are computed. Ionic has used a custom wrapper for handling events that would kebab case event names. That is no longer needed and removing it fixes observed issues. Reproduction case working: https://stackblitz.com/edit/vj18czas-wdhzxjom?file=package.json ## What is the new behavior? We have received a fix for this in https://github.com/stenciljs/output-targets/pull/617 which I hope will resolve this issue by updating the dependency. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information Dev build: `8.4.4-dev.11741193800.14916f6f` --- core/package-lock.json | 15 +++++------ core/package.json | 2 +- core/src/global/ionic-global.ts | 14 +---------- core/src/utils/config.ts | 3 --- core/src/utils/helpers.ts | 28 +-------------------- packages/vue/package-lock.json | 14 +++++------ packages/vue/package.json | 2 +- packages/vue/src/ionic-vue.ts | 27 +------------------- packages/vue/src/utils/overlays.ts | 40 +++++++++++++++++------------- 9 files changed, 43 insertions(+), 102 deletions(-) diff --git a/core/package-lock.json b/core/package-lock.json index d8e0ef6210..0c59317e80 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -28,7 +28,7 @@ "@stencil/angular-output-target": "^0.10.0", "@stencil/react-output-target": "0.5.3", "@stencil/sass": "^3.0.9", - "@stencil/vue-output-target": "^0.9.0", + "@stencil/vue-output-target": "^0.9.6", "@types/jest": "^29.5.6", "@types/node": "^14.6.0", "@typescript-eslint/eslint-plugin": "^6.7.2", @@ -1850,10 +1850,11 @@ } }, "node_modules/@stencil/vue-output-target": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.2.tgz", - "integrity": "sha512-AeBmfo8bQhtob4VKpYTNiCoqh50MeXUwRgYLyO/JxRgAAK9GSfenNrUxXDrK0DK65SWsx/GCOsRwWbfOveorOQ==", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.6.tgz", + "integrity": "sha512-IxLknP+bZ2Di3EOEtd8ozh/9JHyoFyvfO+gapO7IpSjT1zFoEfIpFN0/IZPKN6VNI5lMrQ3BFIRs9C689g7VsQ==", "dev": true, + "license": "MIT", "peerDependencies": { "@stencil/core": ">=2.0.0 || >=3 || >= 4.0.0-beta.0 || >= 4.0.0", "vue": "^3.4.38" @@ -11834,9 +11835,9 @@ "requires": {} }, "@stencil/vue-output-target": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.2.tgz", - "integrity": "sha512-AeBmfo8bQhtob4VKpYTNiCoqh50MeXUwRgYLyO/JxRgAAK9GSfenNrUxXDrK0DK65SWsx/GCOsRwWbfOveorOQ==", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.6.tgz", + "integrity": "sha512-IxLknP+bZ2Di3EOEtd8ozh/9JHyoFyvfO+gapO7IpSjT1zFoEfIpFN0/IZPKN6VNI5lMrQ3BFIRs9C689g7VsQ==", "dev": true, "requires": {} }, diff --git a/core/package.json b/core/package.json index 80db8b924e..1e668023aa 100644 --- a/core/package.json +++ b/core/package.json @@ -50,7 +50,7 @@ "@stencil/angular-output-target": "^0.10.0", "@stencil/react-output-target": "0.5.3", "@stencil/sass": "^3.0.9", - "@stencil/vue-output-target": "^0.9.0", + "@stencil/vue-output-target": "^0.9.6", "@types/jest": "^29.5.6", "@types/node": "^14.6.0", "@typescript-eslint/eslint-plugin": "^6.7.2", diff --git a/core/src/global/ionic-global.ts b/core/src/global/ionic-global.ts index 955b3a6cb7..86ae5878a5 100644 --- a/core/src/global/ionic-global.ts +++ b/core/src/global/ionic-global.ts @@ -1,4 +1,4 @@ -import { getMode, setMode, setPlatformHelpers } from '@stencil/core'; +import { getMode, setMode } from '@stencil/core'; import type { IonicConfig, Mode } from '../interface'; import { isPlatform, setupPlatforms } from '../utils/platform'; @@ -22,18 +22,6 @@ export const initialize = (userConfig: IonicConfig = {}) => { const win = window; const Ionic = ((win as any).Ionic = (win as any).Ionic || {}); - const platformHelpers: any = {}; - if (userConfig._ael) { - platformHelpers.ael = userConfig._ael; - } - if (userConfig._rel) { - platformHelpers.rel = userConfig._rel; - } - if (userConfig._ce) { - platformHelpers.ce = userConfig._ce; - } - setPlatformHelpers(platformHelpers); - // create the Ionic.config from raw config object (if it exists) // and convert Ionic.config into a ConfigApi that has a get() fn const configObj = { diff --git a/core/src/utils/config.ts b/core/src/utils/config.ts index e38d43beb4..e9f963e9c6 100644 --- a/core/src/utils/config.ts +++ b/core/src/utils/config.ts @@ -234,9 +234,6 @@ export interface IonicConfig { _forceStatusbarPadding?: boolean; _testing?: boolean; _zoneGate?: (h: () => any) => any; - _ael?: (el: any, name: string, cb: any, opts: any) => any; - _rel?: (el: any, name: string, cb: any, opts: any) => any; - _ce?: (eventName: string, opts: any) => any; } type FocusManagerPriority = 'content' | 'heading' | 'banner'; diff --git a/core/src/utils/helpers.ts b/core/src/utils/helpers.ts index a005686b77..1a3cd15b0d 100644 --- a/core/src/utils/helpers.ts +++ b/core/src/utils/helpers.ts @@ -42,7 +42,7 @@ const transitionEnd = (el: HTMLElement | null, expectedDuration = 0, callback: ( if (el) { el.addEventListener('webkitTransitionEnd', onTransitionEnd, opts); el.addEventListener('transitionend', onTransitionEnd, opts); - animationTimeout = setTimeout(onTransitionEnd, expectedDuration + ANIMATION_FALLBACK_TIMEOUT); + animationTimeout = setTimeout(onTransitionEnd, expectedDuration + ANIMATION_FALLBACK_TIMEOUT) as unknown as number; unRegTrans = () => { if (animationTimeout !== undefined) { @@ -190,36 +190,10 @@ export const inheritAriaAttributes = (el: HTMLElement, ignoreList?: string[]) => }; export const addEventListener = (el: any, eventName: string, callback: any, opts?: any) => { - if (typeof (window as any) !== 'undefined') { - const win = window as any; - const config = win?.Ionic?.config; - if (config) { - const ael = config.get('_ael'); - if (ael) { - return ael(el, eventName, callback, opts); - } else if (config._ael) { - return config._ael(el, eventName, callback, opts); - } - } - } - return el.addEventListener(eventName, callback, opts); }; export const removeEventListener = (el: any, eventName: string, callback: any, opts?: any) => { - if (typeof (window as any) !== 'undefined') { - const win = window as any; - const config = win?.Ionic?.config; - if (config) { - const rel = config.get('_rel'); - if (rel) { - return rel(el, eventName, callback, opts); - } else if (config._rel) { - return config._rel(el, eventName, callback, opts); - } - } - } - return el.removeEventListener(eventName, callback, opts); }; diff --git a/packages/vue/package-lock.json b/packages/vue/package-lock.json index 95e52a1606..2e788303de 100644 --- a/packages/vue/package-lock.json +++ b/packages/vue/package-lock.json @@ -18,7 +18,7 @@ "@ionic/prettier-config": "^2.0.0", "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-typescript": "^11.1.5", - "@stencil/vue-output-target": "0.9.4", + "@stencil/vue-output-target": "0.9.6", "@typescript-eslint/eslint-plugin": "^5.48.2", "@typescript-eslint/parser": "^5.48.2", "change-case": "^4.1.1", @@ -691,9 +691,9 @@ } }, "node_modules/@stencil/vue-output-target": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.4.tgz", - "integrity": "sha512-nXt1ZKjQ8n+ZaKbj1gcutqcgt7SCwVYzNxa1LfKpKz4L1DST33k1/goahvFeWO/lJzLm47spPtHfcjeaLUg/iQ==", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.6.tgz", + "integrity": "sha512-IxLknP+bZ2Di3EOEtd8ozh/9JHyoFyvfO+gapO7IpSjT1zFoEfIpFN0/IZPKN6VNI5lMrQ3BFIRs9C689g7VsQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4342,9 +4342,9 @@ "integrity": "sha512-WPrTHFngvN081RY+dJPneKQLwnOFD60OMCOQGmmSHfCW0f4ujPMzzhwWU1gcSwXPWXz5O+8cBiiCaxAbJU7kAg==" }, "@stencil/vue-output-target": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.4.tgz", - "integrity": "sha512-nXt1ZKjQ8n+ZaKbj1gcutqcgt7SCwVYzNxa1LfKpKz4L1DST33k1/goahvFeWO/lJzLm47spPtHfcjeaLUg/iQ==", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.9.6.tgz", + "integrity": "sha512-IxLknP+bZ2Di3EOEtd8ozh/9JHyoFyvfO+gapO7IpSjT1zFoEfIpFN0/IZPKN6VNI5lMrQ3BFIRs9C689g7VsQ==", "dev": true, "requires": {} }, diff --git a/packages/vue/package.json b/packages/vue/package.json index fc4240f429..832914d145 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -54,7 +54,7 @@ "@ionic/prettier-config": "^2.0.0", "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-typescript": "^11.1.5", - "@stencil/vue-output-target": "0.9.4", + "@stencil/vue-output-target": "0.9.6", "@typescript-eslint/eslint-plugin": "^5.48.2", "@typescript-eslint/parser": "^5.48.2", "change-case": "^4.1.1", diff --git a/packages/vue/src/ionic-vue.ts b/packages/vue/src/ionic-vue.ts index 0f10108f21..5e188772c0 100644 --- a/packages/vue/src/ionic-vue.ts +++ b/packages/vue/src/ionic-vue.ts @@ -2,25 +2,6 @@ import type { IonicConfig } from "@ionic/core/components"; import { initialize } from "@ionic/core/components"; import type { App, Plugin } from "vue"; -// TODO(FW-2969): types - -const toKebabCase = (eventName: string) => { - return eventName - .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2") - .toLowerCase(); -}; - -const getHelperFunctions = () => { - return { - ael: (el: any, eventName: string, cb: any, opts: any) => - el.addEventListener(toKebabCase(eventName), cb, opts), - rel: (el: any, eventName: string, cb: any, opts: any) => - el.removeEventListener(toKebabCase(eventName), cb, opts), - ce: (eventName: string, opts: any) => - new CustomEvent(toKebabCase(eventName), opts), - }; -}; - export const IonicVue: Plugin<[IonicConfig?]> = { async install(_: App, config: IonicConfig = {}) { /** @@ -34,12 +15,6 @@ export const IonicVue: Plugin<[IonicConfig?]> = { document.documentElement.classList.add("ion-ce"); } - const { ael, rel, ce } = getHelperFunctions(); - initialize({ - ...config, - _ael: ael, - _rel: rel, - _ce: ce, - }); + initialize(config); }, }; diff --git a/packages/vue/src/utils/overlays.ts b/packages/vue/src/utils/overlays.ts index 0e2a4baeed..91133e3490 100644 --- a/packages/vue/src/utils/overlays.ts +++ b/packages/vue/src/utils/overlays.ts @@ -20,10 +20,10 @@ export const defineOverlayContainer = ( const createControllerComponent = (options: ComponentOptions) => { return defineComponent((props, { slots, emit }) => { const eventListeners = [ - { componentEv: `${name}-will-present`, frameworkEv: "willPresent" }, - { componentEv: `${name}-did-present`, frameworkEv: "didPresent" }, - { componentEv: `${name}-will-dismiss`, frameworkEv: "willDismiss" }, - { componentEv: `${name}-did-dismiss`, frameworkEv: "didDismiss" }, + { componentEv: `${name}WillPresent`, frameworkEv: "willPresent" }, + { componentEv: `${name}DidPresent`, frameworkEv: "didPresent" }, + { componentEv: `${name}WillDismiss`, frameworkEv: "willDismiss" }, + { componentEv: `${name}DidDismiss`, frameworkEv: "didDismiss" }, ]; if (defineCustomElement !== undefined) { @@ -139,7 +139,7 @@ export const defineOverlayContainer = ( }, options); }; const createInlineComponent = (options: any) => { - return defineComponent((props, { slots }) => { + return defineComponent((props, { slots, emit }) => { if (defineCustomElement !== undefined) { defineCustomElement(); } @@ -147,18 +147,24 @@ export const defineOverlayContainer = ( const elementRef = ref(); onMounted(() => { - elementRef.value.addEventListener( - "ion-mount", - () => (isOpen.value = true) - ); - elementRef.value.addEventListener( - "will-present", - () => (isOpen.value = true) - ); - elementRef.value.addEventListener( - "did-dismiss", - () => (isOpen.value = false) - ); + elementRef.value.addEventListener("ionMount", (ev: Event) => { + emit("ionMount", ev); + isOpen.value = true; + }); + elementRef.value.addEventListener("willPresent", (ev: Event) => { + emit("willPresent", ev); + isOpen.value = true; + }); + elementRef.value.addEventListener("didDismiss", (ev: Event) => { + emit("didDismiss", ev); + isOpen.value = false; + }); + elementRef.value.addEventListener("willDismiss", (ev: Event) => { + emit("willDismiss", ev); + }); + elementRef.value.addEventListener("didPresent", (ev: Event) => { + emit("didPresent", ev); + }); }); return () => {