mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 02:31:34 +08:00
fix(react): improve component compatibility with preact (#24132)
resolves #23516
This commit is contained in:
@ -95,6 +95,7 @@ export const config: Config = {
|
||||
'ion-popover',
|
||||
'ion-toast',
|
||||
|
||||
'ion-app',
|
||||
'ion-icon'
|
||||
]
|
||||
}),
|
||||
|
@ -13,51 +13,52 @@ type Props = LocalJSX.IonApp &
|
||||
ref?: React.Ref<HTMLIonAppElement>;
|
||||
};
|
||||
|
||||
export class IonApp extends React.Component<Props> {
|
||||
addOverlayCallback?: (id: string, overlay: any, containerElement: HTMLDivElement) => void;
|
||||
removeOverlayCallback?: (id: string) => void;
|
||||
export const IonApp = /*@__PURE__*/ (() =>
|
||||
class extends React.Component<Props> {
|
||||
addOverlayCallback?: (id: string, overlay: any, containerElement: HTMLDivElement) => void;
|
||||
removeOverlayCallback?: (id: string) => void;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
/*
|
||||
Wire up methods to call into IonOverlayManager
|
||||
*/
|
||||
ionContext: IonContextInterface = {
|
||||
addOverlay: (
|
||||
id: string,
|
||||
overlay: ReactComponentOrElement,
|
||||
containerElement: HTMLDivElement
|
||||
) => {
|
||||
if (this.addOverlayCallback) {
|
||||
this.addOverlayCallback(id, overlay, containerElement);
|
||||
}
|
||||
},
|
||||
removeOverlay: (id: string) => {
|
||||
if (this.removeOverlayCallback) {
|
||||
this.removeOverlayCallback(id);
|
||||
}
|
||||
},
|
||||
};
|
||||
/*
|
||||
Wire up methods to call into IonOverlayManager
|
||||
*/
|
||||
ionContext: IonContextInterface = {
|
||||
addOverlay: (
|
||||
id: string,
|
||||
overlay: ReactComponentOrElement,
|
||||
containerElement: HTMLDivElement
|
||||
) => {
|
||||
if (this.addOverlayCallback) {
|
||||
this.addOverlayCallback(id, overlay, containerElement);
|
||||
}
|
||||
},
|
||||
removeOverlay: (id: string) => {
|
||||
if (this.removeOverlayCallback) {
|
||||
this.removeOverlayCallback(id);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<IonContext.Provider value={this.ionContext}>
|
||||
<IonAppInner {...this.props}>{this.props.children}</IonAppInner>
|
||||
<IonOverlayManager
|
||||
onAddOverlay={(callback) => {
|
||||
this.addOverlayCallback = callback;
|
||||
}}
|
||||
onRemoveOverlay={(callback) => {
|
||||
this.removeOverlayCallback = callback;
|
||||
}}
|
||||
/>
|
||||
</IonContext.Provider>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<IonContext.Provider value={this.ionContext}>
|
||||
<IonAppInner {...this.props}>{this.props.children}</IonAppInner>
|
||||
<IonOverlayManager
|
||||
onAddOverlay={(callback) => {
|
||||
this.addOverlayCallback = callback;
|
||||
}}
|
||||
onRemoveOverlay={(callback) => {
|
||||
this.removeOverlayCallback = callback;
|
||||
}}
|
||||
/>
|
||||
</IonContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
return 'IonApp';
|
||||
}
|
||||
}
|
||||
static get displayName() {
|
||||
return 'IonApp';
|
||||
}
|
||||
})();
|
||||
|
@ -12,37 +12,38 @@ type Props = LocalJSX.IonTabButton &
|
||||
onClick?: (e: any) => void;
|
||||
};
|
||||
|
||||
export class IonTabButton extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.handleIonTabButtonClick = this.handleIonTabButtonClick.bind(this);
|
||||
}
|
||||
export const IonTabButton = /*@__PURE__*/ (() =>
|
||||
class extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.handleIonTabButtonClick = this.handleIonTabButtonClick.bind(this);
|
||||
}
|
||||
|
||||
handleIonTabButtonClick() {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(
|
||||
new CustomEvent('ionTabButtonClick', {
|
||||
detail: {
|
||||
tab: this.props.tab,
|
||||
href: this.props.href,
|
||||
routeOptions: this.props.routerOptions,
|
||||
},
|
||||
})
|
||||
handleIonTabButtonClick() {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(
|
||||
new CustomEvent('ionTabButtonClick', {
|
||||
detail: {
|
||||
tab: this.props.tab,
|
||||
href: this.props.href,
|
||||
routeOptions: this.props.routerOptions,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onClick, ...rest } = this.props;
|
||||
return (
|
||||
<IonTabButtonInner
|
||||
onIonTabButtonClick={this.handleIonTabButtonClick}
|
||||
{...rest}
|
||||
></IonTabButtonInner>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onClick, ...rest } = this.props;
|
||||
return (
|
||||
<IonTabButtonInner
|
||||
onIonTabButtonClick={this.handleIonTabButtonClick}
|
||||
{...rest}
|
||||
></IonTabButtonInner>
|
||||
);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
return 'IonTabButton';
|
||||
}
|
||||
}
|
||||
static get displayName() {
|
||||
return 'IonTabButton';
|
||||
}
|
||||
})();
|
||||
|
@ -56,125 +56,126 @@ const tabsInner: React.CSSProperties = {
|
||||
contain: 'layout size style',
|
||||
};
|
||||
|
||||
export class IonTabs extends React.Component<Props> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
routerOutletRef: React.Ref<HTMLIonRouterOutletElement> = React.createRef();
|
||||
selectTabHandler?: (tag: string) => boolean;
|
||||
tabBarRef = React.createRef<any>();
|
||||
export const IonTabs = /*@__PURE__*/ (() =>
|
||||
class extends React.Component<Props> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
routerOutletRef: React.Ref<HTMLIonRouterOutletElement> = React.createRef();
|
||||
selectTabHandler?: (tag: string) => boolean;
|
||||
tabBarRef = React.createRef<any>();
|
||||
|
||||
ionTabContextState: IonTabsContextState = {
|
||||
activeTab: undefined,
|
||||
selectTab: () => false,
|
||||
};
|
||||
ionTabContextState: IonTabsContextState = {
|
||||
activeTab: undefined,
|
||||
selectTab: () => false,
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.tabBarRef.current) {
|
||||
// Grab initial value
|
||||
this.ionTabContextState.activeTab = this.tabBarRef.current.state.activeTab;
|
||||
// Override method
|
||||
this.tabBarRef.current.setActiveTabOnContext = (tab: string) => {
|
||||
this.ionTabContextState.activeTab = tab;
|
||||
};
|
||||
this.ionTabContextState.selectTab = this.tabBarRef.current.selectTab;
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let outlet: React.ReactElement<{}> | undefined;
|
||||
let tabBar: React.ReactElement | undefined;
|
||||
const { className, onIonTabsDidChange, onIonTabsWillChange, ...props } = this.props;
|
||||
|
||||
const children =
|
||||
typeof this.props.children === 'function'
|
||||
? (this.props.children as ChildFunction)(this.ionTabContextState)
|
||||
: this.props.children;
|
||||
|
||||
React.Children.forEach(children, (child: any) => {
|
||||
if (child == null || typeof child !== 'object' || !child.hasOwnProperty('type')) {
|
||||
return;
|
||||
}
|
||||
if (child.type === IonRouterOutlet || child.type.isRouterOutlet) {
|
||||
outlet = React.cloneElement(child, { tabs: true });
|
||||
} else if (child.type === Fragment && child.props.children[0].type === IonRouterOutlet) {
|
||||
outlet = child.props.children[0];
|
||||
componentDidMount() {
|
||||
if (this.tabBarRef.current) {
|
||||
// Grab initial value
|
||||
this.ionTabContextState.activeTab = this.tabBarRef.current.state.activeTab;
|
||||
// Override method
|
||||
this.tabBarRef.current.setActiveTabOnContext = (tab: string) => {
|
||||
this.ionTabContextState.activeTab = tab;
|
||||
};
|
||||
this.ionTabContextState.selectTab = this.tabBarRef.current.selectTab;
|
||||
}
|
||||
}
|
||||
|
||||
let childProps: any = {
|
||||
ref: this.tabBarRef
|
||||
}
|
||||
render() {
|
||||
let outlet: React.ReactElement<{}> | undefined;
|
||||
let tabBar: React.ReactElement | undefined;
|
||||
const { className, onIonTabsDidChange, onIonTabsWillChange, ...props } = this.props;
|
||||
|
||||
/**
|
||||
* Only pass these props
|
||||
* down from IonTabs to IonTabBar
|
||||
* if they are defined, otherwise
|
||||
* if you have a handler set on
|
||||
* IonTabBar it will be overridden.
|
||||
*/
|
||||
if (onIonTabsDidChange !== undefined) {
|
||||
childProps = {
|
||||
...childProps,
|
||||
onIonTabsDidChange
|
||||
const children =
|
||||
typeof this.props.children === 'function'
|
||||
? (this.props.children as ChildFunction)(this.ionTabContextState)
|
||||
: this.props.children;
|
||||
|
||||
React.Children.forEach(children, (child: any) => {
|
||||
if (child == null || typeof child !== 'object' || !child.hasOwnProperty('type')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (onIonTabsWillChange !== undefined) {
|
||||
childProps = {
|
||||
...childProps,
|
||||
onIonTabsWillChange
|
||||
if (child.type === IonRouterOutlet || child.type.isRouterOutlet) {
|
||||
outlet = React.cloneElement(child, { tabs: true });
|
||||
} else if (child.type === Fragment && child.props.children[0].type === IonRouterOutlet) {
|
||||
outlet = child.props.children[0];
|
||||
}
|
||||
|
||||
let childProps: any = {
|
||||
ref: this.tabBarRef
|
||||
}
|
||||
|
||||
/**
|
||||
* Only pass these props
|
||||
* down from IonTabs to IonTabBar
|
||||
* if they are defined, otherwise
|
||||
* if you have a handler set on
|
||||
* IonTabBar it will be overridden.
|
||||
*/
|
||||
if (onIonTabsDidChange !== undefined) {
|
||||
childProps = {
|
||||
...childProps,
|
||||
onIonTabsDidChange
|
||||
}
|
||||
}
|
||||
|
||||
if (onIonTabsWillChange !== undefined) {
|
||||
childProps = {
|
||||
...childProps,
|
||||
onIonTabsWillChange
|
||||
}
|
||||
}
|
||||
|
||||
if (child.type === IonTabBar || child.type.isTabBar) {
|
||||
tabBar = React.cloneElement(child, childProps);
|
||||
} else if (
|
||||
child.type === Fragment &&
|
||||
(child.props.children[1].type === IonTabBar || child.props.children[1].type.isTabBar)
|
||||
) {
|
||||
tabBar = React.cloneElement(child.props.children[1], childProps);
|
||||
}
|
||||
});
|
||||
|
||||
if (!outlet) {
|
||||
throw new Error('IonTabs must contain an IonRouterOutlet');
|
||||
}
|
||||
if (!tabBar) {
|
||||
throw new Error('IonTabs needs a IonTabBar');
|
||||
}
|
||||
|
||||
if (child.type === IonTabBar || child.type.isTabBar) {
|
||||
tabBar = React.cloneElement(child, childProps);
|
||||
} else if (
|
||||
child.type === Fragment &&
|
||||
(child.props.children[1].type === IonTabBar || child.props.children[1].type.isTabBar)
|
||||
) {
|
||||
tabBar = React.cloneElement(child.props.children[1], childProps);
|
||||
}
|
||||
});
|
||||
|
||||
if (!outlet) {
|
||||
throw new Error('IonTabs must contain an IonRouterOutlet');
|
||||
}
|
||||
if (!tabBar) {
|
||||
throw new Error('IonTabs needs a IonTabBar');
|
||||
}
|
||||
|
||||
return (
|
||||
<IonTabsContext.Provider value={this.ionTabContextState}>
|
||||
{this.context.hasIonicRouter() ? (
|
||||
<PageManager
|
||||
className={className ? `${className}` : ''}
|
||||
routeInfo={this.context.routeInfo}
|
||||
{...props}
|
||||
>
|
||||
<ion-tabs className="ion-tabs" style={hostStyles}>
|
||||
return (
|
||||
<IonTabsContext.Provider value={this.ionTabContextState}>
|
||||
{this.context.hasIonicRouter() ? (
|
||||
<PageManager
|
||||
className={className ? `${className}` : ''}
|
||||
routeInfo={this.context.routeInfo}
|
||||
{...props}
|
||||
>
|
||||
<ion-tabs className="ion-tabs" style={hostStyles}>
|
||||
{tabBar.props.slot === 'top' ? tabBar : null}
|
||||
<div style={tabsInner} className="tabs-inner">
|
||||
{outlet}
|
||||
</div>
|
||||
{tabBar.props.slot === 'bottom' ? tabBar : null}
|
||||
</ion-tabs>
|
||||
</PageManager>
|
||||
) : (
|
||||
<div className={className ? `${className}` : 'ion-tabs'} {...props} style={hostStyles}>
|
||||
{tabBar.props.slot === 'top' ? tabBar : null}
|
||||
<div style={tabsInner} className="tabs-inner">
|
||||
{outlet}
|
||||
</div>
|
||||
{tabBar.props.slot === 'bottom' ? tabBar : null}
|
||||
</ion-tabs>
|
||||
</PageManager>
|
||||
) : (
|
||||
<div className={className ? `${className}` : 'ion-tabs'} {...props} style={hostStyles}>
|
||||
{tabBar.props.slot === 'top' ? tabBar : null}
|
||||
<div style={tabsInner} className="tabs-inner">
|
||||
{outlet}
|
||||
</div>
|
||||
{tabBar.props.slot === 'bottom' ? tabBar : null}
|
||||
</div>
|
||||
)}
|
||||
</IonTabsContext.Provider>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</IonTabsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
}
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
||||
|
@ -7,7 +7,6 @@ import type { JSX } from '@ionic/core/components';
|
||||
|
||||
import { IonAccordion as IonAccordionCmp } from '@ionic/core/components/ion-accordion.js';
|
||||
import { IonAccordionGroup as IonAccordionGroupCmp } from '@ionic/core/components/ion-accordion-group.js';
|
||||
import { IonApp as IonAppCmp } from '@ionic/core/components/ion-app.js';
|
||||
import { IonAvatar as IonAvatarCmp } from '@ionic/core/components/ion-avatar.js';
|
||||
import { IonBackdrop as IonBackdropCmp } from '@ionic/core/components/ion-backdrop.js';
|
||||
import { IonBadge as IonBadgeCmp } from '@ionic/core/components/ion-badge.js';
|
||||
@ -76,7 +75,6 @@ import { IonVirtualScroll as IonVirtualScrollCmp } from '@ionic/core/components/
|
||||
|
||||
export const IonAccordion = /*@__PURE__*/createReactComponent<JSX.IonAccordion, HTMLIonAccordionElement>('ion-accordion', undefined, undefined, IonAccordionCmp);
|
||||
export const IonAccordionGroup = /*@__PURE__*/createReactComponent<JSX.IonAccordionGroup, HTMLIonAccordionGroupElement>('ion-accordion-group', undefined, undefined, IonAccordionGroupCmp);
|
||||
export const IonApp = /*@__PURE__*/createReactComponent<JSX.IonApp, HTMLIonAppElement>('ion-app', undefined, undefined, IonAppCmp);
|
||||
export const IonAvatar = /*@__PURE__*/createReactComponent<JSX.IonAvatar, HTMLIonAvatarElement>('ion-avatar', undefined, undefined, IonAvatarCmp);
|
||||
export const IonBackdrop = /*@__PURE__*/createReactComponent<JSX.IonBackdrop, HTMLIonBackdropElement>('ion-backdrop', undefined, undefined, IonBackdropCmp);
|
||||
export const IonBadge = /*@__PURE__*/createReactComponent<JSX.IonBadge, HTMLIonBadgeElement>('ion-badge', undefined, undefined, IonBadgeCmp);
|
||||
|
Reference in New Issue
Block a user