diff --git a/core/stencil.config.ts b/core/stencil.config.ts index d2bfe23e302..eb47ece352b 100644 --- a/core/stencil.config.ts +++ b/core/stencil.config.ts @@ -83,6 +83,7 @@ export const config: Config = { 'ion-popover', 'ion-toast', + 'ion-app', 'ion-icon' ], routerLinkComponents: [ diff --git a/packages/vue/src/components/IonApp.ts b/packages/vue/src/components/IonApp.ts new file mode 100644 index 00000000000..b45a8811ebb --- /dev/null +++ b/packages/vue/src/components/IonApp.ts @@ -0,0 +1,38 @@ +import { h, defineComponent, shallowRef, VNode } from 'vue'; + +const userComponents = shallowRef([]); +export const IonApp = defineComponent({ + name: 'IonApp', + setup(_, { attrs, slots }) { + return () => { + return h( + 'ion-app', + { + ...attrs + }, + [slots.default && slots.default(), ...userComponents.value] + ) + } + } +}); + +/** + * When rendering user components inside of + * ion-modal, ion-popover, or ion-nav, the component + * needs to be created inside of the current application + * context otherwise libraries such as vue-i18n or vuex + * will not work properly. + * + * `userComponents` renders teleported components as children + * of `ion-app` within the current application context. + */ +export const addTeleportedUserComponent = (component: VNode) => { + userComponents.value = [ + ...userComponents.value, + component + ] +} + +export const removeTeleportedUserComponent = (component: VNode) => { + userComponents.value = userComponents.value.filter(cmp => cmp !== component); +} diff --git a/packages/vue/src/framework-delegate.ts b/packages/vue/src/framework-delegate.ts index c00bed361dd..8a8cfd12de1 100644 --- a/packages/vue/src/framework-delegate.ts +++ b/packages/vue/src/framework-delegate.ts @@ -1,7 +1,8 @@ -import { createVNode, render } from 'vue'; - +import { h, Teleport, VNode } from 'vue'; +import { addTeleportedUserComponent, removeTeleportedUserComponent } from './components/IonApp'; export const VueDelegate = () => { - const attachViewToDom = (parentElement: HTMLElement, component: any, componentProps: any, classes?: string[]) => { + let Component: VNode | undefined; + const attachViewToDom = (parentElement: HTMLElement, component: any, componentProps: any = {}, classes?: string[]) => { /** * Ionic Framework passes in modal and popover element * refs as props, but if these are not defined @@ -10,20 +11,24 @@ export const VueDelegate = () => { */ delete componentProps['modal']; delete componentProps['popover']; - const vueInstance = createVNode(component, componentProps); const div = document.createElement('div'); classes && div.classList.add(...classes); - parentElement.appendChild(div); - render(vueInstance, div); + Component = h( + Teleport, + { to: div }, + h(component, { ...componentProps }) + ); + + addTeleportedUserComponent(Component); return div; } - const removeViewFromDom = (_: HTMLElement, childElement: any) => { - render(null, childElement); + const removeViewFromDom = () => { + Component && removeTeleportedUserComponent(Component); return Promise.resolve(); } diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 0d9778ea873..a73e44b349d 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -12,6 +12,8 @@ export { IonTabs } from './components/IonTabs'; export { IonTabBar } from './components/IonTabBar'; export { IonNav } from './components/IonNav'; export { IonIcon } from './components/IonIcon'; +export { IonApp } from './components/IonApp'; + export * from './components/Overlays'; export { IonKeyboardRef, IonRouter, useBackButton, useIonRouter, useKeyboard } from './hooks'; diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index d6bb116c032..510c79ae0ce 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -8,9 +8,6 @@ import { JSX } from '@ionic/core'; -export const IonApp = /*@__PURE__*/ defineContainer('ion-app'); - - export const IonAvatar = /*@__PURE__*/ defineContainer('ion-avatar'); diff --git a/packages/vue/src/vue-component-lib/overlays.ts b/packages/vue/src/vue-component-lib/overlays.ts index 98df87ed2f2..12c661c9cfd 100644 --- a/packages/vue/src/vue-component-lib/overlays.ts +++ b/packages/vue/src/vue-component-lib/overlays.ts @@ -16,8 +16,6 @@ export const defineOverlayContainer = (name: string, compo const Container = defineComponent((props, { slots, emit }) => { const overlay = ref(); - const content = ref(); - const onVnodeMounted = async () => { const isOpen = props.isOpen; isOpen && (await present(props)) @@ -39,7 +37,7 @@ export const defineOverlayContainer = (name: string, compo } const present = async (props: Readonly) => { - const component = (slots) ? h('div', { ref: content }, slots) : undefined; + const component = slots.default && slots.default()[0]; overlay.value = await controller.create({ ...props, component diff --git a/packages/vue/test-app/package-lock.json b/packages/vue/test-app/package-lock.json index 581180c1797..4369858b2dc 100644 --- a/packages/vue/test-app/package-lock.json +++ b/packages/vue/test-app/package-lock.json @@ -1306,27 +1306,27 @@ } }, "@ionic/core": { - "version": "5.4.0-dev.202010091911.bfc0b25", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.4.0-dev.202010091911.bfc0b25.tgz", - "integrity": "sha512-gDWwMcgV+kPeTfBToZ8oLoSI7FygkRYEgi/DEAIHye3W8T/Z/NmqrxaNqXrXU0J0RKvfzrIlrw6uB67Xz1xRcw==", + "version": "5.4.0-dev.202010081857.bfc0b25", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.4.0-dev.202010081857.bfc0b25.tgz", + "integrity": "sha512-yCH1GTlTTTS3dt9kYk/y5K82UdKOLidRqziGTsFeOoqTJI2w87SgRx0V0Il92dcB1XaWPB/SNnaM7Tt+GD7+Lg==", "requires": { "ionicons": "^5.1.2", "tslib": "^1.10.0" } }, "@ionic/vue": { - "version": "5.4.0-dev.202010091911.bfc0b25", - "resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-5.4.0-dev.202010091911.bfc0b25.tgz", - "integrity": "sha512-zrF/u3sMq4k3mLMtlA/VcS+RFeWEXrhOnzJAo8uPuDGT4ZcttJv0sVIFJMqZF7zM+6+xxep5B5mMoB1cigwG3A==", + "version": "5.4.0-dev.202010081857.bfc0b25", + "resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-5.4.0-dev.202010081857.bfc0b25.tgz", + "integrity": "sha512-A2s0skOjoytlwC2xJVo+jmv6ZcYKV+HAaGitqKs08bPHATFDh+yzpJLXRkS0poZ3N2Bk4ossXsd5gn4eXC7cKw==", "requires": { - "@ionic/core": "5.4.0-dev.202010091911.bfc0b25", + "@ionic/core": "5.4.0-dev.202010081857.bfc0b25", "ionicons": "^5.1.2" } }, "@ionic/vue-router": { - "version": "5.4.0-dev.202010091911.bfc0b25", - "resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-5.4.0-dev.202010091911.bfc0b25.tgz", - "integrity": "sha512-moa388WPxJjHBcIVILoijS+xrTcS7jaK4T/rP3U9Lri1bSYrDpYNWmd/0sktGt80m3gaa8VXS5E995zDiyLAfQ==" + "version": "5.4.0-dev.202010081857.bfc0b25", + "resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-5.4.0-dev.202010081857.bfc0b25.tgz", + "integrity": "sha512-qlFRY32CuttCZHqFFTZoDOERFFLDursJvMeb75KjE5lO/gD+dbelb7Cn0dOyaKyrM06X/lwb8eXgjCwSsGbHxg==" }, "@jest/console": { "version": "24.9.0", diff --git a/packages/vue/test-app/package.json b/packages/vue/test-app/package.json index 57cb3352056..63abd67fa2d 100644 --- a/packages/vue/test-app/package.json +++ b/packages/vue/test-app/package.json @@ -12,8 +12,8 @@ "sync": "sh ./scripts/sync.sh" }, "dependencies": { - "@ionic/vue": "5.4.0-dev.202010091911.bfc0b25", - "@ionic/vue-router": "5.4.0-dev.202010091911.bfc0b25", + "@ionic/vue": "5.4.0-dev.202010081857.bfc0b25", + "@ionic/vue-router": "5.4.0-dev.202010081857.bfc0b25", "core-js": "^3.6.5", "vue": "^3.0.0-0", "vue-router": "^4.0.0-0" diff --git a/packages/vue/test-app/src/components/Nav.vue b/packages/vue/test-app/src/components/Nav.vue new file mode 100644 index 00000000000..f4ca0250940 --- /dev/null +++ b/packages/vue/test-app/src/components/Nav.vue @@ -0,0 +1,16 @@ + + + diff --git a/packages/vue/test-app/src/components/NavChild.vue b/packages/vue/test-app/src/components/NavChild.vue new file mode 100644 index 00000000000..6208bc7b395 --- /dev/null +++ b/packages/vue/test-app/src/components/NavChild.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/vue/test-app/src/components/NavRoot.vue b/packages/vue/test-app/src/components/NavRoot.vue new file mode 100644 index 00000000000..ec2fe1c5131 --- /dev/null +++ b/packages/vue/test-app/src/components/NavRoot.vue @@ -0,0 +1,47 @@ + + + diff --git a/packages/vue/test-app/src/router/index.ts b/packages/vue/test-app/src/router/index.ts index c7b94ed0f8e..be47e97907c 100644 --- a/packages/vue/test-app/src/router/index.ts +++ b/packages/vue/test-app/src/router/index.ts @@ -27,13 +27,16 @@ const routes: Array = [ component: () => import('@/views/DefaultHref.vue') }, { - path: '/navigation', - name: 'Navigation', - component: () => import('@/views/Navigation.vue') + path: '/routing', + component: () => import('@/views/Routing.vue') }, { - path: '/navigation/child', - component: () => import('@/views/NavigationChild.vue') + path: '/routing/child', + component: () => import('@/views/RoutingChild.vue') + }, + { + path: '/navigation', + component: () => import('@/views/Navigation.vue') }, { path: '/nested', @@ -81,7 +84,7 @@ const routes: Array = [ component: () => import('@/views/Tab3.vue') } ] - } + }, ] const router = createRouter({ diff --git a/packages/vue/test-app/src/views/Home.vue b/packages/vue/test-app/src/views/Home.vue index 168551e97af..fa10900a606 100644 --- a/packages/vue/test-app/src/views/Home.vue +++ b/packages/vue/test-app/src/views/Home.vue @@ -26,6 +26,9 @@ Navigation + + Routing + Default Href diff --git a/packages/vue/test-app/src/views/Navigation.vue b/packages/vue/test-app/src/views/Navigation.vue index 0a01ac07d34..33bbb08a879 100644 --- a/packages/vue/test-app/src/views/Navigation.vue +++ b/packages/vue/test-app/src/views/Navigation.vue @@ -16,56 +16,47 @@ - - Set Route Parameters - - - - Go to Child Page - +
+ Open Modal +
diff --git a/packages/vue/test-app/src/views/Overlays.vue b/packages/vue/test-app/src/views/Overlays.vue index a6ce253bca2..b79df4df884 100644 --- a/packages/vue/test-app/src/views/Overlays.vue +++ b/packages/vue/test-app/src/views/Overlays.vue @@ -90,6 +90,7 @@ @@ -97,6 +98,7 @@ @@ -231,6 +233,10 @@ export default defineComponent({ } ] + const overlayProps = { + title: 'Custom Title' + } + const openActionSheet = async () => { const actionSheet = await actionSheetController.create({ buttons: actionSheetButtons }); await actionSheet.present(); @@ -252,12 +258,12 @@ export default defineComponent({ } const openModal = async () => { - const modal = await modalController.create({ component: ModalContent }); + const modal = await modalController.create({ component: ModalContent, componentProps: overlayProps }); await modal.present(); } const openPopover = async (event: Event) => { - const popover = await popoverController.create({ component: PopoverContent, event }); + const popover = await popoverController.create({ component: PopoverContent, event, componentProps: overlayProps }); await popover.present(); } @@ -309,6 +315,7 @@ export default defineComponent({ } return { + overlayProps, present, componentType, presentationType, diff --git a/packages/vue/test-app/src/views/Routing.vue b/packages/vue/test-app/src/views/Routing.vue new file mode 100644 index 00000000000..f6c3aa5d6f0 --- /dev/null +++ b/packages/vue/test-app/src/views/Routing.vue @@ -0,0 +1,70 @@ + + + diff --git a/packages/vue/test-app/src/views/NavigationChild.vue b/packages/vue/test-app/src/views/RoutingChild.vue similarity index 83% rename from packages/vue/test-app/src/views/NavigationChild.vue rename to packages/vue/test-app/src/views/RoutingChild.vue index 92909bc8aef..8e41b6b7280 100644 --- a/packages/vue/test-app/src/views/NavigationChild.vue +++ b/packages/vue/test-app/src/views/RoutingChild.vue @@ -1,23 +1,23 @@