mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 11:17:19 +08:00
Master react (#18998)
* chore(): bump to beta 8 * fix(): IonFabButton href fix * fix(react): support components with href attributes * fix(): Prep work to break router out * fix(): breaking react-router and react-core into own packages * chore(): moving view stuff out of react-core * chore(): dev build 8-1 * chore(): update to react beta 8 * chore(): fixes to deps * fix(): removing IonAnchor in favor of IonRouterLink * chore(): beta 9 release * refactor(react): treeshake, minify, api * wip * fix(): react dev builds * fix(): fixes to get app builds working again * fix(): removing tgz file * feat(): adding platform helper methods * fix(): don't map attributes to props * chore(): add test app * feat(): copy css folder from core * chore(): move rollup node resolve to devDependencies * fix(): expose setupConfig() * perf(): improve treeshaking * fix(): removing crypto from generateUniqueId * fix(): adding missing rollup dp * fix(): test cleanup and fixes to make tests pass * chore(): moving react to packages folder * fix(): fixing react build due to move to packages * feat(): adding missing IonInfiniteScrollContent component * chore(): add automated testing using cypress * fix(): adding option onDidDismiss to controller components * 0.0.10 react * wip * fix(): removing deprecated React calls * fix(): exporting setupConfig from core * chore(): bump to 4.8.0-rc.0 * chore(): updating test-app deps and fixing test * chore(): updates to react readme
This commit is contained in:
39
packages/react/src/components/navigation/IonBackButton.tsx
Normal file
39
packages/react/src/components/navigation/IonBackButton.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { JSX as LocalJSX } from '@ionic/core';
|
||||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../../contexts/NavContext';
|
||||
import { IonBackButtonInner } from '../inner-proxies';
|
||||
|
||||
type Props = LocalJSX.IonBackButton & {
|
||||
ref?: React.RefObject<HTMLIonBackButtonElement>;
|
||||
};
|
||||
|
||||
export const IonBackButton = /*@__PURE__*/(() => class extends React.Component<Props> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
|
||||
clickButton = (e: MouseEvent) => {
|
||||
const defaultHref = this.props.defaultHref;
|
||||
if (defaultHref !== undefined) {
|
||||
if (this.context.hasIonicRouter()) {
|
||||
e.stopPropagation();
|
||||
this.context.goBack(defaultHref);
|
||||
} else {
|
||||
window.location.href = defaultHref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<IonBackButtonInner onClick={this.clickButton} {...this.props}></IonBackButtonInner>
|
||||
);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
return 'IonBackButton';
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
111
packages/react/src/components/navigation/IonTabBar.tsx
Normal file
111
packages/react/src/components/navigation/IonTabBar.tsx
Normal file
@ -0,0 +1,111 @@
|
||||
import { JSX as LocalJSX } from '@ionic/core';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { NavContext } from '../../contexts/NavContext';
|
||||
import { IonTabBarInner } from '../inner-proxies';
|
||||
import { IonTabButton } from '../proxies';
|
||||
|
||||
type Props = LocalJSX.IonTabBar & {
|
||||
ref?: React.RefObject<HTMLIonTabBarElement>;
|
||||
navigate: (path: string) => void;
|
||||
currentPath: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
interface Tab {
|
||||
originalHref: string;
|
||||
currentHref: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
activeTab: string | undefined;
|
||||
tabs: { [key: string]: Tab };
|
||||
}
|
||||
|
||||
const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
const tabActiveUrls: { [key: string]: Tab } = {};
|
||||
|
||||
React.Children.forEach(this.props.children, (child: any) => {
|
||||
if (child != null && typeof child === 'object' && child.props && child.type === IonTabButton) {
|
||||
tabActiveUrls[child.props.tab] = {
|
||||
originalHref: child.props.href,
|
||||
currentHref: child.props.href
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
this.state = {
|
||||
activeTab: undefined,
|
||||
tabs: tabActiveUrls
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props: Props, state: State) {
|
||||
const activeTab = Object.keys(state.tabs)
|
||||
.find(key => {
|
||||
const href = state.tabs[key].originalHref;
|
||||
return props.currentPath.startsWith(href);
|
||||
});
|
||||
|
||||
if (activeTab === undefined || (activeTab === state.activeTab && state.tabs[activeTab].currentHref === props.currentPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
activeTab,
|
||||
tabs: {
|
||||
...state.tabs,
|
||||
[activeTab]: {
|
||||
originalHref: state.tabs[activeTab].originalHref,
|
||||
currentHref: props.currentPath
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private onTabButtonClick = (e: CustomEvent<{ href: string, selected: boolean, tab: string }>) => {
|
||||
const targetUrl = (this.state.activeTab === e.detail.tab) ?
|
||||
this.state.tabs[e.detail.tab].originalHref :
|
||||
this.state.tabs[e.detail.tab].currentHref;
|
||||
this.props.navigate(targetUrl);
|
||||
}
|
||||
|
||||
private renderChild = (activeTab: string | null | undefined) => (child: (React.ReactElement<LocalJSX.IonTabButton & { onIonTabButtonClick: (e: CustomEvent) => void }>) | null | undefined) => {
|
||||
if (child != null && child.props && child.type === IonTabButton) {
|
||||
const href = (child.props.tab === activeTab) ? this.props.currentPath : (this.state.tabs[child.props.tab!].currentHref);
|
||||
|
||||
return React.cloneElement(child, {
|
||||
href,
|
||||
onIonTabButtonClick: this.onTabButtonClick
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<IonTabBarInner {...this.props} selectedTab={this.state.activeTab}>
|
||||
{React.Children.map(this.props.children as any, this.renderChild(this.state.activeTab))}
|
||||
</IonTabBarInner>
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
export const IonTabBar: React.FC<LocalJSX.IonTabBar & { currentPath?: string, navigate?: (path: string) => void; }> = props => {
|
||||
const context = useContext(NavContext);
|
||||
return (
|
||||
<IonTabBarUnwrapped
|
||||
{...props as any}
|
||||
navigate={props.navigate || ((path: string) => {
|
||||
context.navigate(path);
|
||||
})}
|
||||
currentPath={props.currentPath || context.currentPath}
|
||||
>
|
||||
{props.children}
|
||||
</IonTabBarUnwrapped>
|
||||
);
|
||||
};
|
89
packages/react/src/components/navigation/IonTabs.tsx
Normal file
89
packages/react/src/components/navigation/IonTabs.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../../contexts/NavContext';
|
||||
import { IonRouterOutlet } from '../proxies';
|
||||
|
||||
import { IonTabBar } from './IonTabBar';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const hostStyles: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
right: '0',
|
||||
bottom: '0',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
contain: 'layout size style'
|
||||
};
|
||||
|
||||
const tabsInner: React.CSSProperties = {
|
||||
position: 'relative',
|
||||
flex: 1,
|
||||
contain: 'layout size style'
|
||||
};
|
||||
|
||||
export const IonTabs = /*@__PURE__*/(() => class extends React.Component<Props> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
routerOutletRef: React.Ref<HTMLIonRouterOutletElement> = React.createRef();
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let outlet: React.ReactElement<{}> | undefined;
|
||||
let tabBar: React.ReactElement<{ slot: 'bottom' | 'top' }> | undefined;
|
||||
|
||||
React.Children.forEach(this.props.children, (child: any) => {
|
||||
if (child == null || typeof child !== 'object' || !child.hasOwnProperty('type')) {
|
||||
return;
|
||||
}
|
||||
if (child.type === IonRouterOutlet) {
|
||||
outlet = child;
|
||||
}
|
||||
if (child.type === IonTabBar) {
|
||||
tabBar = child;
|
||||
}
|
||||
});
|
||||
|
||||
if (!outlet) {
|
||||
throw new Error('IonTabs must contain an IonRouterOutlet');
|
||||
}
|
||||
if (!tabBar) {
|
||||
// TODO, this is not required
|
||||
throw new Error('IonTabs needs a IonTabBar');
|
||||
}
|
||||
|
||||
const NavManager = this.context.getViewManager();
|
||||
|
||||
return (
|
||||
<div style={hostStyles}>
|
||||
{tabBar.props.slot === 'top' ? tabBar : null}
|
||||
<div style={tabsInner} className="tabs-inner">
|
||||
{this.context.hasIonicRouter() ? (
|
||||
<NavManager>
|
||||
{outlet}
|
||||
</NavManager>
|
||||
) : (
|
||||
<>{outlet}</>
|
||||
)}
|
||||
</div>
|
||||
{tabBar.props.slot === 'bottom' ? tabBar : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
return 'IonTabs';
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
Reference in New Issue
Block a user