mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
merge release-4.11.4
This commit is contained in:
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,17 @@
|
||||
## [4.11.4](https://github.com/ionic-team/ionic/compare/v4.11.1...v4.11.4) (2019-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** check for component unmount, fixes [#19859](https://github.com/ionic-team/ionic/issues/19859) ([7356c40](https://github.com/ionic-team/ionic/commit/7356c401742ce2b3241d6ab05fce0fa65d2f1f8a))
|
||||
* **react:** adding multiple subscriptions to lifecycle events, fixes [#19792](https://github.com/ionic-team/ionic/issues/19792) ([#19858](https://github.com/ionic-team/ionic/issues/19858)) ([0a3014d](https://github.com/ionic-team/ionic/commit/0a3014d35e2102570fd3d8c5ada29eb01aab18e9))
|
||||
* **react:** add check to warn if no ionpage is found, fixes [#19832](https://github.com/ionic-team/ionic/issues/19832) ([#19857](https://github.com/ionic-team/ionic/issues/19857)) ([628e766](https://github.com/ionic-team/ionic/commit/628e76668ea72baebdb02b9dcfe24c0da837fb08))
|
||||
* **react:** expand the location stack to better support back button, fixes [#19748](https://github.com/ionic-team/ionic/issues/19748) ([#19856](https://github.com/ionic-team/ionic/issues/19856)) ([d89508b](https://github.com/ionic-team/ionic/commit/d89508b1b58481d518b89362a8792d05f3f451c9))
|
||||
* **react:** adding hardware back button support, fixes(19819) ([#19851](https://github.com/ionic-team/ionic/issues/19851)) ([fd9745d](https://github.com/ionic-team/ionic/commit/fd9745ddcddded76d64220838aef0f599bf4352f))
|
||||
* **react:** adding swipe back functionality and routerOutlet ready improvements, fixes [#19818](https://github.com/ionic-team/ionic/issues/19818) ([#19849](https://github.com/ionic-team/ionic/issues/19849)) ([bcc40c8](https://github.com/ionic-team/ionic/commit/bcc40c8d59b723bbdb1dfd318bfb2219eb8df3cf))
|
||||
* **react:** create a new overlay each time component is presented, fixes [#19841](https://github.com/ionic-team/ionic/issues/19841), [#19823](https://github.com/ionic-team/ionic/issues/19823) ([#19842](https://github.com/ionic-team/ionic/issues/19842)) ([9fad416](https://github.com/ionic-team/ionic/commit/9fad4161be4859969e14d4d33169ef022052d6bf))
|
||||
|
||||
|
||||
## [4.11.3](https://github.com/ionic-team/ionic/compare/v4.11.1...v4.11.3) (2019-10-30)
|
||||
|
||||
|
||||
|
||||
@@ -385,6 +385,18 @@ describe('cubic-bezier conversion', () => {
|
||||
|
||||
shouldApproximatelyEqual(getTimeGivenProgression(...equation, 1.02), [0.35, 0.87]);
|
||||
})
|
||||
|
||||
it('cubic-bezier(0.32, 0.72, 0, 1) (with out of bounds progression)', () => {
|
||||
const equation = [
|
||||
[0, 0],
|
||||
[0.05, 0.2],
|
||||
[.14, 1.72],
|
||||
[1, 1]
|
||||
];
|
||||
|
||||
expect(getTimeGivenProgression(...equation, 1.32)).toEqual([]);
|
||||
expect(getTimeGivenProgression(...equation, -0.32)).toEqual([]);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -4,24 +4,16 @@ import { Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import { generateId } from '../utils';
|
||||
import { LocationHistory } from '../utils/LocationHistory';
|
||||
|
||||
import { StackManager } from './StackManager';
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { ViewStack } from './ViewStacks';
|
||||
|
||||
interface NavManagerProps extends RouteComponentProps {
|
||||
findViewInfoByLocation: (location: HistoryLocation) => { view?: ViewItem, viewStack?: ViewStack };
|
||||
findViewInfoById: (id: string) => { view?: ViewItem, viewStack?: ViewStack };
|
||||
getActiveIonPage: () => { view?: ViewItem, viewStack?: ViewStack };
|
||||
onNavigateBack: (defaultHref?: string) => void;
|
||||
onNavigate: (type: 'push' | 'replace', path: string, state?: any) => void;
|
||||
}
|
||||
|
||||
export class NavManager extends React.Component<NavManagerProps, NavContextState> {
|
||||
|
||||
listenUnregisterCallback: UnregisterCallback | undefined;
|
||||
locationHistory: LocationHistory = new LocationHistory();
|
||||
|
||||
constructor(props: NavManagerProps) {
|
||||
super(props);
|
||||
@@ -40,16 +32,15 @@ export class NavManager extends React.Component<NavManagerProps, NavContextState
|
||||
this.setState({
|
||||
currentPath: location.pathname
|
||||
});
|
||||
this.locationHistory.add(location);
|
||||
});
|
||||
|
||||
this.locationHistory.add({
|
||||
hash: window.location.hash,
|
||||
key: generateId(),
|
||||
pathname: window.location.pathname,
|
||||
search: window.location.search,
|
||||
state: {}
|
||||
});
|
||||
if (document) {
|
||||
document.addEventListener('ionBackButton', (e: any) => {
|
||||
e.detail.register(0, () => {
|
||||
this.props.history.goBack();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -59,26 +50,7 @@ export class NavManager extends React.Component<NavManagerProps, NavContextState
|
||||
}
|
||||
|
||||
goBack(defaultHref?: string) {
|
||||
const { view: activeIonPage } = this.props.getActiveIonPage();
|
||||
if (activeIonPage) {
|
||||
const { view: enteringView } = this.props.findViewInfoById(activeIonPage.prevId!);
|
||||
if (enteringView) {
|
||||
const lastLocation = this.locationHistory.findLastLocation(enteringView.routeData.match.url);
|
||||
if (lastLocation) {
|
||||
this.props.onNavigate('replace', lastLocation.pathname + lastLocation.search, 'back');
|
||||
} else {
|
||||
this.props.onNavigate('replace', enteringView.routeData.match.url, 'back');
|
||||
}
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.props.onNavigate('replace', defaultHref, 'back');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.props.onNavigate('replace', defaultHref, 'back');
|
||||
}
|
||||
}
|
||||
this.props.onNavigateBack(defaultHref);
|
||||
}
|
||||
|
||||
navigate(path: string, direction?: RouterDirection | 'none') {
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface RouteManagerContextState {
|
||||
syncView: (page: HTMLElement, viewId: string) => void;
|
||||
hideView: (viewId: string) => void;
|
||||
viewStacks: ViewStacks;
|
||||
setupIonRouter: (id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) => Promise<void>;
|
||||
setupIonRouter: (id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) => void;
|
||||
removeViewStack: (stack: string) => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { NavDirection } from '@ionic/core';
|
||||
import { RouterDirection } from '@ionic/react';
|
||||
import { RouterDirection, getConfig } from '@ionic/react';
|
||||
import { Action as HistoryAction, Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, matchPath, withRouter } from 'react-router-dom';
|
||||
|
||||
import { generateId } from '../utils';
|
||||
import { generateId, isDevMode } from '../utils';
|
||||
import { LocationHistory } from '../utils/LocationHistory';
|
||||
|
||||
import { IonRouteData } from './IonRouteData';
|
||||
import { NavManager } from './NavManager';
|
||||
@@ -21,11 +22,13 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
listenUnregisterCallback: UnregisterCallback | undefined;
|
||||
activeIonPageId?: string;
|
||||
currentDirection?: RouterDirection;
|
||||
locationHistory = new LocationHistory();
|
||||
|
||||
constructor(props: RouteComponentProps) {
|
||||
super(props);
|
||||
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
|
||||
this.handleNavigate = this.handleNavigate.bind(this);
|
||||
this.navigateBack = this.navigateBack.bind(this);
|
||||
this.state = {
|
||||
viewStacks: new ViewStacks(),
|
||||
hideView: this.hideView.bind(this),
|
||||
@@ -33,6 +36,14 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
removeViewStack: this.removeViewStack.bind(this),
|
||||
syncView: this.syncView.bind(this)
|
||||
};
|
||||
|
||||
this.locationHistory.add({
|
||||
hash: window.location.hash,
|
||||
key: generateId(),
|
||||
pathname: window.location.pathname,
|
||||
search: window.location.search,
|
||||
state: {}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(_prevProps: RouteComponentProps, prevState: RouteManagerState) {
|
||||
@@ -66,6 +77,13 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
historyChange(location: HistoryLocation, action: HistoryAction) {
|
||||
location.state = location.state || { direction: this.currentDirection };
|
||||
this.currentDirection = undefined;
|
||||
if (action === 'PUSH') {
|
||||
this.locationHistory.add(location);
|
||||
} else if ((action === 'REPLACE' && location.state.direction === 'back') || action === 'POP') {
|
||||
this.locationHistory.pop();
|
||||
} else {
|
||||
this.locationHistory.replace(location);
|
||||
}
|
||||
this.setState({
|
||||
location,
|
||||
action
|
||||
@@ -99,21 +117,22 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
* If the page is being pushed into the stack by another view,
|
||||
* record the view that originally directed to the new view for back button purposes.
|
||||
*/
|
||||
enteringView.prevId = enteringView.prevId || leavingView.id;
|
||||
enteringView.prevId = leavingView.id;
|
||||
} else if (action === 'POP') {
|
||||
direction = leavingView.prevId === enteringView.id ? 'back' : 'none';
|
||||
} else {
|
||||
direction = direction || 'back';
|
||||
leavingView.mount = false;
|
||||
}
|
||||
} else if (direction === 'back' || action === 'REPLACE') {
|
||||
}
|
||||
if (direction === 'back' || action === 'REPLACE') {
|
||||
leavingView.mount = false;
|
||||
this.removeOrphanedViews(enteringView, enteringViewStack);
|
||||
}
|
||||
} else {
|
||||
// If there is not a leavingView, then we shouldn't provide a direction
|
||||
direction = undefined;
|
||||
}
|
||||
this.removeOrphanedViews(enteringView, enteringViewStack);
|
||||
} else {
|
||||
enteringView.show = true;
|
||||
enteringView.mount = true;
|
||||
@@ -139,21 +158,36 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
if (enteringEl) {
|
||||
// Don't animate from an empty view
|
||||
const navDirection = leavingEl && leavingEl.innerHTML === '' ? undefined : direction === 'none' ? undefined : direction;
|
||||
this.transitionView(
|
||||
const shouldGoBack = !!enteringView.prevId || !!this.locationHistory.previous();
|
||||
this.commitView(
|
||||
enteringEl!,
|
||||
leavingEl!,
|
||||
viewStack.routerOutlet,
|
||||
navDirection,
|
||||
!!enteringView.prevId);
|
||||
shouldGoBack);
|
||||
} else if (leavingEl) {
|
||||
leavingEl.classList.add('ion-page-hidden');
|
||||
leavingEl.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
|
||||
// Warn if an IonPage was not eventually rendered in Dev Mode
|
||||
if (isDevMode()) {
|
||||
if (enteringView.routeData.match!.url !== location.pathname) {
|
||||
setTimeout(() => {
|
||||
const { view } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
if (view!.routeData.match!.url !== location.pathname) {
|
||||
console.warn('No IonPage was found to render. Make sure you wrap your page with an IonPage component.');
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeOrphanedViews(view: ViewItem, viewStack: ViewStack) {
|
||||
// Note: This technique is a bit wonky for views that reference each other and get into a circular loop.
|
||||
// It can still remove a view that probably shouldn't be.
|
||||
const viewsToRemove = viewStack.views.filter(v => v.prevId === view.id);
|
||||
viewsToRemove.forEach(v => {
|
||||
// Don't remove if view is currently active
|
||||
@@ -173,7 +207,7 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
});
|
||||
}
|
||||
|
||||
async setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
||||
setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
||||
const views: ViewItem[] = [];
|
||||
let activeId: string | undefined;
|
||||
const ionRouterOutlet = React.Children.only(children) as React.ReactElement;
|
||||
@@ -181,7 +215,7 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
views.push(createViewItem(child, this.props.history.location));
|
||||
});
|
||||
|
||||
await this.registerViewStack(id, activeId, views, routerOutlet, this.props.location);
|
||||
this.registerViewStack(id, activeId, views, routerOutlet, this.props.location);
|
||||
|
||||
function createViewItem(child: React.ReactElement<any>, location: HistoryLocation) {
|
||||
const viewId = generateId();
|
||||
@@ -212,29 +246,61 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
}
|
||||
}
|
||||
|
||||
async registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.setState(prevState => {
|
||||
const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks);
|
||||
const newStack: ViewStack = {
|
||||
id: stack,
|
||||
views: stackItems,
|
||||
routerOutlet
|
||||
};
|
||||
if (activeId) {
|
||||
this.activeIonPageId = activeId;
|
||||
}
|
||||
prevViewStacks.set(stack, newStack);
|
||||
return {
|
||||
viewStacks: prevViewStacks
|
||||
};
|
||||
}, () => {
|
||||
resolve();
|
||||
});
|
||||
registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
|
||||
this.setState(prevState => {
|
||||
const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks);
|
||||
const newStack: ViewStack = {
|
||||
id: stack,
|
||||
views: stackItems,
|
||||
routerOutlet
|
||||
};
|
||||
if (activeId) {
|
||||
this.activeIonPageId = activeId;
|
||||
}
|
||||
prevViewStacks.set(stack, newStack);
|
||||
return {
|
||||
viewStacks: prevViewStacks
|
||||
};
|
||||
}, () => {
|
||||
this.setupRouterOutlet(routerOutlet);
|
||||
});
|
||||
}
|
||||
|
||||
async setupRouterOutlet(routerOutlet: HTMLIonRouterOutletElement) {
|
||||
const waitUntilReady = async () => {
|
||||
if (routerOutlet.componentOnReady) {
|
||||
routerOutlet.dispatchEvent(new Event('routerOutletReady'));
|
||||
return;
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
waitUntilReady();
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
await waitUntilReady();
|
||||
|
||||
const canStart = () => {
|
||||
const config = getConfig();
|
||||
const swipeEnabled = config && config.get('swipeBackEnabled', routerOutlet.mode === 'ios');
|
||||
if (swipeEnabled) {
|
||||
const { view } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
return !!(view && view.prevId);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const onStart = () => {
|
||||
this.navigateBack();
|
||||
};
|
||||
routerOutlet.swipeHandler = {
|
||||
canStart,
|
||||
onStart,
|
||||
onEnd: _shouldContinue => true
|
||||
};
|
||||
}
|
||||
|
||||
removeViewStack(stack: string) {
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
viewStacks.delete(stack);
|
||||
@@ -245,7 +311,6 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
|
||||
syncView(page: HTMLElement, viewId: string) {
|
||||
this.setState(state => {
|
||||
|
||||
const viewStacks = Object.assign(new ViewStacks(), state.viewStacks);
|
||||
const { view } = viewStacks.findViewInfoById(viewId);
|
||||
|
||||
@@ -261,20 +326,6 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
});
|
||||
}
|
||||
|
||||
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet: HTMLIonRouterOutletElement | undefined, direction: NavDirection | undefined, showGoBack: boolean) {
|
||||
/**
|
||||
* Super hacky workaround to make sure ionRouterOutlet is available
|
||||
* since transitionView might be called before IonRouterOutlet is fully mounted
|
||||
*/
|
||||
if (ionRouterOutlet && ionRouterOutlet.componentOnReady) {
|
||||
this.commitView(enteringEl, leavingEl, ionRouterOutlet, direction, showGoBack);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.transitionView(enteringEl, leavingEl, ionRouterOutlet, direction, showGoBack);
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
private async commitView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction?: NavDirection, showGoBack?: boolean) {
|
||||
|
||||
if (enteringEl === leavingEl) {
|
||||
@@ -305,15 +356,41 @@ class RouteManager extends React.Component<RouteComponentProps, RouteManagerStat
|
||||
}
|
||||
}
|
||||
|
||||
navigateBack(defaultHref?: string) {
|
||||
const { view: activeIonPage } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
|
||||
if (activeIonPage) {
|
||||
const { view: enteringView } = this.state.viewStacks.findViewInfoById(activeIonPage.prevId);
|
||||
if (enteringView) {
|
||||
const lastLocation = this.locationHistory.findLastLocationByUrl(enteringView.routeData.match!.url);
|
||||
if (lastLocation) {
|
||||
this.handleNavigate('replace', lastLocation.pathname + lastLocation.search, 'back');
|
||||
} else {
|
||||
this.handleNavigate('replace', enteringView.routeData.match!.url, 'back');
|
||||
}
|
||||
} else {
|
||||
const currentLocation = this.locationHistory.previous();
|
||||
if (currentLocation) {
|
||||
this.handleNavigate('replace', currentLocation.pathname + currentLocation.search, 'back');
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.handleNavigate('replace', defaultHref, 'back');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (defaultHref) {
|
||||
this.handleNavigate('replace', defaultHref, 'back');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RouteManagerContext.Provider value={this.state}>
|
||||
<NavManager
|
||||
{...this.props}
|
||||
onNavigateBack={this.navigateBack}
|
||||
onNavigate={this.handleNavigate}
|
||||
findViewInfoById={(id: string) => this.state.viewStacks.findViewInfoById(id)}
|
||||
findViewInfoByLocation={(location: HistoryLocation) => this.state.viewStacks.findViewInfoByLocation(location)}
|
||||
getActiveIonPage={() => this.state.viewStacks.findViewInfoById(this.activeIonPageId)}
|
||||
>
|
||||
{this.props.children}
|
||||
</NavManager>
|
||||
|
||||
@@ -11,7 +11,11 @@ interface StackManagerProps {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
interface StackManagerState {
|
||||
routerOutletReady: boolean;
|
||||
}
|
||||
|
||||
export class StackManager extends React.Component<StackManagerProps, StackManagerState> {
|
||||
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
|
||||
context!: React.ContextType<typeof RouteManagerContext>;
|
||||
id: string;
|
||||
@@ -21,10 +25,18 @@ export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
this.id = this.props.id || generateId();
|
||||
this.handleViewSync = this.handleViewSync.bind(this);
|
||||
this.handleHideView = this.handleHideView.bind(this);
|
||||
this.state = {
|
||||
routerOutletReady: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.context.setupIonRouter(this.id, this.props.children, this.routerOutletEl.current!);
|
||||
this.routerOutletEl.current!.addEventListener('routerOutletReady', () => {
|
||||
this.setState({
|
||||
routerOutletReady: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -51,8 +63,9 @@ export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
const viewStack = context.viewStacks.get(this.id);
|
||||
const views = (viewStack || { views: [] }).views.filter(x => x.show);
|
||||
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
|
||||
const { routerOutletReady } = this.state;
|
||||
|
||||
const childElements = views.map(view => {
|
||||
const childElements = routerOutletReady ? views.map(view => {
|
||||
return (
|
||||
<ViewTransitionManager
|
||||
id={view.id}
|
||||
@@ -68,7 +81,7 @@ export class StackManager extends React.Component<StackManagerProps, {}> {
|
||||
</View>
|
||||
</ViewTransitionManager>
|
||||
);
|
||||
});
|
||||
}) : <div></div>;
|
||||
|
||||
const elementProps: any = {
|
||||
ref: this.routerOutletEl
|
||||
|
||||
@@ -12,9 +12,30 @@ export class LocationHistory {
|
||||
}
|
||||
}
|
||||
|
||||
findLastLocation(url: string) {
|
||||
const reversedLocations = [...this.locationHistory].reverse();
|
||||
const last = reversedLocations.find(x => x.pathname.toLowerCase() === url.toLowerCase());
|
||||
return last;
|
||||
pop() {
|
||||
this.locationHistory.pop();
|
||||
}
|
||||
|
||||
replace(location: HistoryLocation) {
|
||||
this.locationHistory.pop();
|
||||
this.locationHistory.push(location);
|
||||
}
|
||||
|
||||
findLastLocationByUrl(url: string) {
|
||||
for (let i = this.locationHistory.length - 1; i >= 0; i--) {
|
||||
const location = this.locationHistory[i];
|
||||
if (location.pathname.toLocaleLowerCase() === url.toLocaleLowerCase()) {
|
||||
return location;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
previous() {
|
||||
return this.locationHistory[this.locationHistory.length - 2];
|
||||
}
|
||||
|
||||
current() {
|
||||
return this.locationHistory[this.locationHistory.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"no-invalid-template-strings": true,
|
||||
"ban-export-const-enum": true,
|
||||
"only-arrow-functions": false,
|
||||
"strict-boolean-conditions": [true, "allow-null-union", "allow-undefined-union", "allow-boolean-or-undefined", "allow-string"],
|
||||
"strict-boolean-conditions": [false],
|
||||
"jsx-key": false,
|
||||
"jsx-self-close": false,
|
||||
"jsx-curly-spacing": [true, "never"],
|
||||
|
||||
@@ -23,6 +23,7 @@ export const createControllerComponent = <OptionsType extends object, OverlayTyp
|
||||
|
||||
return class extends React.Component<Props> {
|
||||
overlay?: OverlayType;
|
||||
isUnmounted = false;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -40,6 +41,7 @@ export const createControllerComponent = <OptionsType extends object, OverlayTyp
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isUnmounted = true;
|
||||
if (this.overlay) { this.overlay.dismiss(); }
|
||||
}
|
||||
|
||||
@@ -54,18 +56,16 @@ export const createControllerComponent = <OptionsType extends object, OverlayTyp
|
||||
|
||||
async present(prevProps?: Props) {
|
||||
const { isOpen, onDidDismiss, ...cProps } = this.props;
|
||||
let overlay = this.overlay;
|
||||
if (!overlay) {
|
||||
overlay = this.overlay = await controller.create({
|
||||
...cProps as any
|
||||
});
|
||||
}
|
||||
attachProps(overlay, {
|
||||
this.overlay = await controller.create({
|
||||
...cProps as any
|
||||
});
|
||||
attachProps(this.overlay, {
|
||||
[dismissEventName]: onDidDismiss
|
||||
}, prevProps);
|
||||
// Check isOpen again since the value could of changed during the async call to controller.create
|
||||
if (this.props.isOpen === true) {
|
||||
await overlay.present();
|
||||
// Check isOpen again since the value could have changed during the async call to controller.create
|
||||
// It's also possible for the component to have become unmounted.
|
||||
if (this.props.isOpen === true && this.isUnmounted === false) {
|
||||
await this.overlay.present();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export { IonBackButton } from './navigation/IonBackButton';
|
||||
export { IonRouterOutlet } from './IonRouterOutlet';
|
||||
|
||||
// Utils
|
||||
export { isPlatform, getPlatforms } from './utils';
|
||||
export { isPlatform, getPlatforms, getConfig } from './utils';
|
||||
export { RouterDirection } from './hrefprops';
|
||||
|
||||
// Icons that are used by internal components
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Platforms, getPlatforms as getPlatformsCore, isPlatform as isPlatformCore } from '@ionic/core';
|
||||
import { Config as CoreConfig, Platforms, getPlatforms as getPlatformsCore, isPlatform as isPlatformCore } from '@ionic/core';
|
||||
import React from 'react';
|
||||
|
||||
import { IonicReactProps } from '../IonicReactProps';
|
||||
@@ -24,3 +24,13 @@ export const isPlatform = (platform: Platforms) => {
|
||||
export const getPlatforms = () => {
|
||||
return getPlatformsCore(window);
|
||||
};
|
||||
|
||||
export const getConfig = (): CoreConfig | null => {
|
||||
if (typeof (window as any) !== 'undefined') {
|
||||
const Ionic = (window as any).Ionic;
|
||||
if (Ionic && Ionic.config) {
|
||||
return Ionic.config;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -22,52 +22,46 @@ export const IonLifeCycleContext = /*@__PURE__*/React.createContext<IonLifeCycle
|
||||
ionViewDidLeave: () => { return; },
|
||||
});
|
||||
|
||||
type LifeCycleCallback = () => void;
|
||||
|
||||
export const DefaultIonLifeCycleContext = class implements IonLifeCycleContextInterface {
|
||||
|
||||
ionViewWillEnterCallback?: () => void;
|
||||
ionViewDidEnterCallback?: () => void;
|
||||
ionViewWillLeaveCallback?: () => void;
|
||||
ionViewDidLeaveCallback?: () => void;
|
||||
ionViewWillEnterCallbacks: LifeCycleCallback[] = [];
|
||||
ionViewDidEnterCallbacks: LifeCycleCallback[] = [];
|
||||
ionViewWillLeaveCallbacks: LifeCycleCallback[] = [];
|
||||
ionViewDidLeaveCallbacks: LifeCycleCallback[] = [];
|
||||
componentCanBeDestroyedCallback?: () => void;
|
||||
|
||||
onIonViewWillEnter(callback: () => void) {
|
||||
this.ionViewWillEnterCallback = callback;
|
||||
onIonViewWillEnter(callback: LifeCycleCallback) {
|
||||
this.ionViewWillEnterCallbacks.push(callback);
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
if (this.ionViewWillEnterCallback) {
|
||||
this.ionViewWillEnterCallback();
|
||||
}
|
||||
this.ionViewWillEnterCallbacks.forEach(cb => cb());
|
||||
}
|
||||
|
||||
onIonViewDidEnter(callback: () => void) {
|
||||
this.ionViewDidEnterCallback = callback;
|
||||
onIonViewDidEnter(callback: LifeCycleCallback) {
|
||||
this.ionViewDidEnterCallbacks.push(callback);
|
||||
}
|
||||
|
||||
ionViewDidEnter() {
|
||||
if (this.ionViewDidEnterCallback) {
|
||||
this.ionViewDidEnterCallback();
|
||||
}
|
||||
this.ionViewDidEnterCallbacks.forEach(cb => cb());
|
||||
}
|
||||
|
||||
onIonViewWillLeave(callback: () => void) {
|
||||
this.ionViewWillLeaveCallback = callback;
|
||||
onIonViewWillLeave(callback: LifeCycleCallback) {
|
||||
this.ionViewWillLeaveCallbacks.push(callback);
|
||||
}
|
||||
|
||||
ionViewWillLeave() {
|
||||
if (this.ionViewWillLeaveCallback) {
|
||||
this.ionViewWillLeaveCallback();
|
||||
}
|
||||
this.ionViewWillLeaveCallbacks.forEach(cb => cb());
|
||||
}
|
||||
|
||||
onIonViewDidLeave(callback: () => void) {
|
||||
this.ionViewDidLeaveCallback = callback;
|
||||
onIonViewDidLeave(callback: LifeCycleCallback) {
|
||||
this.ionViewDidLeaveCallbacks.push(callback);
|
||||
}
|
||||
|
||||
ionViewDidLeave() {
|
||||
if (this.ionViewDidLeaveCallback) {
|
||||
this.ionViewDidLeaveCallback();
|
||||
}
|
||||
this.ionViewDidLeaveCallbacks.forEach(cb => cb());
|
||||
this.componentCanBeDestroyed();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
import { useContext } from 'react';
|
||||
import { useContext, useEffect } from 'react';
|
||||
|
||||
import { IonLifeCycleContext } from '../contexts/IonLifeCycleContext';
|
||||
|
||||
export const useIonViewWillEnter = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewWillEnter(callback);
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
useEffect(() => {
|
||||
context.onIonViewWillEnter(callback);
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useIonViewDidEnter = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewDidEnter(callback);
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
useEffect(() => {
|
||||
context.onIonViewDidEnter(callback);
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useIonViewWillLeave = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewWillLeave(callback);
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
useEffect(() => {
|
||||
context.onIonViewWillLeave(callback);
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useIonViewDidLeave = (callback: () => void) => {
|
||||
const value = useContext(IonLifeCycleContext);
|
||||
value.onIonViewDidLeave(callback);
|
||||
const context = useContext(IonLifeCycleContext);
|
||||
useEffect(() => {
|
||||
context.onIonViewDidLeave(callback);
|
||||
}, []);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user