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