refactor(vue): drop support for "on" prefixed overlay events and bump minimum required version of vue to 3.0.6 (#23229)

refactor(vue): drop support for "on" prefixed overlay events and bump minimum required version of vue to 3.0.6

BREAKING CHANGE:

- Dropped support for prefixed overlay events in favor of non prefixed events (I.e. `@onDidDismiss` becomes `@didDismiss`).
- Minimum required version of Vue is now Vue v3.0.6 or newer.
This commit is contained in:
Liam DeBeasi
2021-04-23 13:06:22 -04:00
committed by GitHub
parent 51c62fafe5
commit 6fcb3a62b1
6 changed files with 221 additions and 140 deletions

View File

@ -1,7 +1,6 @@
import { App, Plugin } from 'vue';
import { IonicConfig, setupConfig } from '@ionic/core';
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
import { needsKebabCase } from './utils';
/**
* We need to make sure that the web component fires an event
@ -9,31 +8,22 @@ import { needsKebabCase } from './utils';
* otherwise the binding's callback will fire before any
* v-model values have been updated.
*/
const toLowerCase = (eventName: string) => eventName === 'ionChange' ? 'v-ionchange' : eventName.toLowerCase();
const toKebabCase = (eventName: string) => eventName === 'ionChange' ? 'v-ion-change' : eventName.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
/**
* Vue 3.0.6 fixed a bug where events on custom elements
* were always converted to lower case, so "ionRefresh"
* became "ionRefresh". We need to account for the old
* issue as well as the new behavior where "ionRefresh"
* is converted to "ion-refresh".
* See https://github.com/vuejs/vue-next/pull/2847
*/
const getHelperFunctions = (needsKebabCase: boolean = true) => {
const conversionFn = (needsKebabCase) ? toKebabCase : toLowerCase;
const getHelperFunctions = () => {
return {
ael: (el: any, eventName: string, cb: any, opts: any) => el.addEventListener(conversionFn(eventName), cb, opts),
rel: (el: any, eventName: string, cb: any, opts: any) => el.removeEventListener(conversionFn(eventName), cb, opts),
ce: (eventName: string, opts: any) => new CustomEvent(conversionFn(eventName), opts)
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 = {
async install(app: App, config: IonicConfig = {}) {
async install(_: App, config: IonicConfig = {}) {
if (typeof (window as any) !== 'undefined') {
const { ael, rel, ce } = getHelperFunctions(needsKebabCase(app.version));
const { ael, rel, ce } = getHelperFunctions();
setupConfig({
...config,
_ael: ael,

View File

@ -57,5 +57,3 @@ export const getConfig = (): CoreConfig | null => {
}
return null;
};
export const needsKebabCase = (version: string) => !['3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5'].includes(version);

View File

@ -1,72 +1,18 @@
import { defineComponent, h, ref, VNode, getCurrentInstance, ComponentInternalInstance } from 'vue';
import { needsKebabCase } from '../utils';
import { defineComponent, h, ref, VNode } from 'vue';
export interface OverlayProps {
isOpen?: boolean;
}
/**
* Make sure we only
* warn user about each
* event at most once.
*/
let willPresentWarn = false;
let didPresentWarn = false;
let willDismissWarn = false;
let didDismissWarn = false;
const checkForDeprecatedListeners = (instance: ComponentInternalInstance) => {
const props = instance.vnode.props;
if (!willPresentWarn && props.onOnWillPresent !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onWillPresent has been deprecated in favor of @willPresent and will be removed in Ionic Vue v6.0.');
willPresentWarn = true;
}
if (!didPresentWarn && props.onOnDidPresent !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onDidPresent has been deprecated in favor of @didPresent and will be removed in Ionic Vue v6.0.');
didPresentWarn = true;
}
if (!willDismissWarn && props.onOnWillDismiss !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onWillDismiss has been deprecated in favor of @willDismiss and will be removed in Ionic Vue v6.0.');
willDismissWarn = true;
}
if (!didDismissWarn && props.onOnDidDismiss !== undefined) {
console.warn('[@ionic/vue Deprecation]: @onDidDismiss has been deprecated in favor of @didDismiss and will be removed in Ionic Vue v6.0.');
didDismissWarn = true;
}
}
export const defineOverlayContainer = <Props extends object>(name: string, componentProps: string[] = [], controller: any) => {
/**
* Vue 3.0.6 fixed a bug where events on custom elements
* were always converted to lower case, so "ionRefresh"
* became "ionrefresh". We need to account for the old
* issue as well as the new behavior where "ionRefresh"
* is converted to "ion-refresh".
* See https://github.com/vuejs/vue-next/pull/2847
*/
const eventPrefix = name.toLowerCase().split('-').join('');
const lowerCaseListeners = [
{ componentEv: `${eventPrefix}willpresent`, frameworkEv: 'willPresent', deprecatedEv: 'onWillPresent' },
{ componentEv: `${eventPrefix}didpresent`, frameworkEv: 'didPresent', deprecatedEv: 'onDidPresent' },
{ componentEv: `${eventPrefix}willdismiss`, frameworkEv: 'willDismiss', deprecatedEv: 'onWillDismiss' },
{ componentEv: `${eventPrefix}diddismiss`, frameworkEv: 'didDismiss', deprecatedEv: 'onDidDismiss' },
];
const kebabCaseListeners = [
{ componentEv: `${name}-will-present`, frameworkEv: 'willPresent', deprecatedEv: 'onWillPresent' },
{ componentEv: `${name}-did-present`, frameworkEv: 'didPresent', deprecatedEv: 'onDidPresent' },
{ componentEv: `${name}-will-dismiss`, frameworkEv: 'willDismiss', deprecatedEv: 'onWillDismiss' },
{ componentEv: `${name}-did-dismiss`, frameworkEv: 'didDismiss', deprecatedEv: 'onDidDismiss' },
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' },
];
const Container = defineComponent<Props & OverlayProps>((props, { slots, emit }) => {
const instance = getCurrentInstance();
const adjustedEventListeners = needsKebabCase(instance.appContext.app.version) ? kebabCaseListeners : lowerCaseListeners;
checkForDeprecatedListeners(instance);
const overlay = ref();
const onVnodeMounted = async () => {
const isOpen = props.isOpen;
@ -121,10 +67,8 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
}
/**
* When supporting both the "on" prefixed and non-"on" prefixed
* events, there seems to be an issue where the handlers are
* getting passed as props. This should be resolved when we drop
* support for the "on" prefixed listeners.
* These are getting passed as props.
* Potentially a Vue bug with Web Components?
*/
const restOfProps = { ...(props as any) };
delete restOfProps.onWillPresent;
@ -140,10 +84,9 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
overlay.value = await overlay.value;
adjustedEventListeners.forEach(eventListener => {
eventListeners.forEach(eventListener => {
overlay.value.addEventListener(eventListener.componentEv, () => {
emit(eventListener.frameworkEv);
emit(eventListener.deprecatedEv);
});
})
@ -166,10 +109,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
Container.displayName = name;
Container.props = [...componentProps, 'isOpen'];
Container.emits = [
'willPresent', 'didPresent', 'willDismiss', 'didDismiss',
'onWillPresent', 'onDidPresent', 'onWillDismiss', 'onDidDismiss'
];
Container.emits = ['willPresent', 'didPresent', 'willDismiss', 'didDismiss'];
return Container;
}