fix(react): IonNav works with react (#25565)

Resolves #24002 

Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
This commit is contained in:
Sean Perkins
2022-07-06 13:45:30 -04:00
committed by GitHub
parent ffb0311236
commit 420f9bbebd
9 changed files with 197 additions and 1 deletions

View File

@ -132,6 +132,7 @@ export { IonPopover } from './IonPopover';
// Custom Components
export { IonApp } from './IonApp';
export { IonPage } from './IonPage';
export { IonNav } from './navigation/IonNav';
export { IonTabsContext, IonTabsContextState } from './navigation/IonTabsContext';
export { IonTabs } from './navigation/IonTabs';
export { IonTabBar } from './navigation/IonTabBar';

View File

@ -21,7 +21,15 @@ export const IonBackButton = /*@__PURE__*/ (() =>
context!: React.ContextType<typeof NavContext>;
clickButton = (e: React.MouseEvent) => {
/**
* If ion-back-button is being used inside
* of ion-nav then we should not interact with
* the router.
*/
if (e.target && (e.target as HTMLElement).closest('ion-nav') !== null) { return; }
const { defaultHref, routerAnimation } = this.props;
if (this.context.hasIonicRouter()) {
e.stopPropagation();
this.context.goBack(defaultHref, routerAnimation);

View File

@ -0,0 +1,30 @@
import type { FrameworkDelegate, JSX } from '@ionic/core/components';
import { defineCustomElement } from '@ionic/core/components/ion-nav.js';
import React, { useState } from 'react';
import { ReactDelegate } from '../../framework-delegate';
import { createReactComponent } from '../react-component-lib';
const IonNavInner = createReactComponent<
JSX.IonNav & { delegate: FrameworkDelegate },
HTMLIonNavElement
>('ion-nav', undefined, undefined, defineCustomElement);
export const IonNav: React.FC<JSX.IonNav> = ({ children, ...restOfProps }) => {
const [views, setViews] = useState<React.ReactPortal[]>([]);
/**
* Allows us to create React components that are rendered within
* the context of the IonNav component.
*/
const addView = (view: React.ReactPortal) => setViews([...views, view]);
const removeView = (view: React.ReactPortal) => setViews(views.filter((v) => v !== view));
const delegate = ReactDelegate(addView, removeView);
return (
<IonNavInner delegate={delegate} {...restOfProps}>
{views}
</IonNavInner>
);
};

View File

@ -0,0 +1,38 @@
import { FrameworkDelegate } from '@ionic/core/components';
import { createPortal } from 'react-dom';
export const ReactDelegate = (
addView: (view: React.ReactPortal) => void,
removeView: (view: React.ReactPortal) => void
): FrameworkDelegate => {
let Component: React.ReactPortal;
const attachViewToDom = async (
parentElement: HTMLElement,
component: () => JSX.Element,
propsOrDataObj?: any,
cssClasses?: string[]
): Promise<any> => {
const div = document.createElement('div');
cssClasses && div.classList.add(...cssClasses);
parentElement.appendChild(div);
Component = createPortal(component(), div);
Component.props = propsOrDataObj;
addView(Component);
return Promise.resolve(div);
};
const removeViewFromDom = (): Promise<void> => {
Component && removeView(Component);
return Promise.resolve();
};
return {
attachViewToDom,
removeViewFromDom,
};
};