Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ionic/react

Package Overview
Dependencies
Maintainers
19
Versions
2899
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ionic/react - npm Package Compare versions

Comparing version 0.0.6-3 to 0.0.6-4

dist/types/components/navigation/IonRouter.d.ts

547

dist/index.esm.js

@@ -1,3 +0,1 @@

import { addIcons } from 'ionicons';
import { ICON_PATHS } from 'ionicons/icons';
import { defineCustomElements } from '@ionic/core/loader';

@@ -9,3 +7,4 @@ import { __rest } from 'tslib';

export { setupConfig } from '@ionic/core';
import { withRouter, matchPath } from 'react-router';
import { withRouter, Switch, matchPath } from 'react-router';
import { matchPath as matchPath$1, Redirect, withRouter as withRouter$1 } from 'react-router-dom';

@@ -408,3 +407,3 @@ function attachEventProps(node, newProps, oldProps = {}) {

class StackItemInternal extends React.Component {
class ViewInternal extends React.Component {
constructor(props) {

@@ -417,3 +416,3 @@ super(props);

componentDidMount() {
const { forwardedRef, activateView } = this.props;
const { forwardedRef } = this.props;
this.setState({ ref: forwardedRef });

@@ -425,5 +424,2 @@ if (forwardedRef && forwardedRef.current) {

forwardedRef.current.addEventListener('ionViewDidLeave', this.ionViewDidLeaveHandler.bind(this));
if (activateView) {
activateView(forwardedRef.current);
}
}

@@ -453,3 +449,3 @@ }

render() {
const _a = this.props, { className, children, forwardedRef, activateView } = _a, rest = __rest(_a, ["className", "children", "forwardedRef", "activateView"]);
const _a = this.props, { className, children, forwardedRef } = _a, rest = __rest(_a, ["className", "children", "forwardedRef"]);
const { ref } = this.state;

@@ -459,12 +455,20 @@ return (React.createElement("div", Object.assign({ className: className ? `ion-page ${className}` : 'ion-page', ref: forwardedRef }, rest), ref && children));

}
StackItemInternal.contextType = IonLifeCycleContext;
ViewInternal.contextType = IonLifeCycleContext;
function forwardRef$1(props, ref) {
return React.createElement(StackItemInternal, Object.assign({ forwardedRef: ref }, props));
return React.createElement(ViewInternal, Object.assign({ forwardedRef: ref }, props));
}
forwardRef$1.displayName = 'StackItem';
const StackItem = /*@__PURE__*/ React.forwardRef(forwardRef$1);
forwardRef$1.displayName = 'View';
const View = /*@__PURE__*/ React.forwardRef(forwardRef$1);
const NavContext = /*@__PURE__*/ React.createContext({
goBack: () => { }
viewStacks: {},
hideView: () => { navContextNotFoundError(); },
goBack: () => { navContextNotFoundError(); },
registerViewStack: () => { navContextNotFoundError(); },
removeViewStack: () => { navContextNotFoundError(); },
transitionView: () => { navContextNotFoundError(); }
});
function navContextNotFoundError() {
console.error('IonRouter not found, did you add it to the app?');
}

@@ -525,3 +529,3 @@ const withIonLifeCycle = (WrappedComponent) => {

class StackItemManager extends React.Component {
class ViewItemManager extends React.Component {
constructor(props) {

@@ -536,13 +540,9 @@ super(props);

if (!this.props.mount) {
/**
* Give child component time to finish calling its
* own onViewDidLeave before destroying it
*/
setTimeout(() => {
if (this._isMounted) {
this.setState({
show: false
});
}
}, 1000);
if (this._isMounted) {
this.setState({
show: false
}, () => {
this.context.hideView(this.props.id);
});
}
}

@@ -563,172 +563,55 @@ });

// TODO: treeshake
StackItemManager.contextType = IonLifeCycleContext;
ViewItemManager.contextType = NavContext;
class RouterOutlet extends React.Component {
class IonRouterOutletUnWrapped extends React.Component {
constructor(props) {
super(props);
this.enteringEl = React.createRef();
this.leavingEl = React.createRef();
this.containerEl = React.createRef();
this.inTransition = false;
this.goBack = (defaultHref) => {
const prevView = this.state.views.find(v => v.id === this.state.activeId);
const newView = this.state.views.find(v => v.id === prevView.prevId);
if (newView) {
this.props.history.replace(newView.location.pathname || defaultHref);
}
else {
this.id = this.props.id || generateUniqueId();
}
componentDidMount() {
const views = [];
let activeId;
React.Children.forEach(this.props.children, (child) => {
if (child.type === Switch) {
/**
* find the parent view based on the defaultHref and add it
* to the views collection so that navigation works properly
* If the first child is a Switch, loop through its children to build the viewStack
*/
let element;
let match;
React.Children.forEach(this.props.children, (child) => {
if (match == null) {
element = child;
match = matchPath(defaultHref, child.props);
}
React.Children.forEach(child.props.children, (grandChild) => {
addView.call(this, grandChild);
});
if (element && match) {
const viewId = generateUniqueId();
const parentView = {
id: viewId,
location: {
pathname: defaultHref
},
element: element,
match: match,
prevId: undefined,
mount: true
};
prevView.prevId = viewId;
this.setState({
views: [parentView, prevView]
});
}
this.props.history.replace(defaultHref);
}
};
this.state = {
direction: undefined,
activeId: undefined,
prevActiveId: undefined,
tabActiveIds: {},
views: []
};
this.activateView = this.activateView.bind(this);
}
static getDerivedStateFromProps(props, state) {
const location = props.location;
let match = null;
let element;
/**
* Remove any views that have been unmounted previously
*/
const views = state.views.filter(x => x.mount === true);
/**
* Get the current active view and if the path is the same then do nothing
*/
const activeView = views.find(v => v.id === state.activeId);
/**
* Look at all available paths and find the one that matches
*/
React.Children.forEach(props.children, (child) => {
if (match == null) {
element = child;
match = matchPath(location.pathname, child.props);
else {
addView.call(this, child);
}
});
/**
* If there are no matches then set the active view to null and exit
*/
if (!match) {
return {
direction: undefined,
activeId: undefined,
prevActiveId: undefined
this.context.registerViewStack(this.id, activeId, views, this.containerEl.current, this.props.location);
function addView(child) {
const location = this.props.history.location;
const viewId = generateUniqueId();
const key = generateUniqueId();
const element = child;
const match = matchPath(location.pathname, child.props);
const view = {
id: viewId,
key,
match,
element,
mount: true,
show: !!match,
ref: React.createRef(),
childProps: child.props
};
}
/**
* Get the active view for the tab that matches.
* If the location matches the existing tab path then set that view as active
*/
const id = state.tabActiveIds[match.params.tab];
const currentActiveTabView = views.find(v => v.id === id);
if (currentActiveTabView && currentActiveTabView.location.pathname === props.location.pathname) {
if (currentActiveTabView.id === state.activeId) {
/**
* The current tab was clicked, so do nothing
*/
return null;
if (!!match) {
activeId = viewId;
}
/**
* Activate a tab that is already in views
*/
return {
direction: undefined,
activeId: currentActiveTabView.id,
prevActiveId: state.activeId,
views: views
};
views.push(view);
return activeId;
}
/**
* If the new active view is a previous view, then animate it back in
*/
if (activeView) {
const prevActiveView = views.find(v => v.id === activeView.prevId);
if (prevActiveView && activeView.match.params.tab === match.params.tab && prevActiveView.match.url === match.url) {
return {
direction: 'back',
activeId: prevActiveView.id,
prevActiveId: activeView.id,
tabActiveIds: Object.assign({}, state.tabActiveIds, { [match.params.tab]: prevActiveView.id }),
views: views.map(x => {
if (x.id === activeView.id) {
return Object.assign({}, x, { mount: false });
}
return x;
})
};
}
}
/**
* If the current view does not match the url, see if the view that matches the url is currently in the stack.
* If so, show the view that matches the url and remove the current view.
*/
if (currentActiveTabView && currentActiveTabView.location.pathname !== props.location.pathname) {
const view = views.find(x => x.location.pathname == props.location.pathname);
if (view && view.id === currentActiveTabView.prevId) {
return {
direction: undefined,
activeId: view.id,
prevActiveId: undefined,
views: views.filter(x => x.id !== currentActiveTabView.id),
tabActiveIds: Object.assign({}, state.tabActiveIds, { [match.params.tab]: view.id }),
};
}
}
/**
* Else add this new view to the stack
*/
const viewId = generateUniqueId();
const newState = {
direction: (state.tabActiveIds[match.params.tab]) ? 'forward' : undefined,
activeId: viewId,
prevActiveId: state.tabActiveIds[match.params.tab] || state.activeId,
tabActiveIds: Object.assign({}, state.tabActiveIds, { [match.params.tab]: viewId }),
views: views.concat({
id: viewId,
location,
match,
element,
prevId: state.tabActiveIds[match.params.tab],
mount: true
})
};
return newState;
}
componentWillUnmount() {
this.context.removeViewStack(this.id);
}
renderChild(item) {
const component = React.cloneElement(item.element, {
location: item.location,
computedMatch: item.match

@@ -738,73 +621,15 @@ });

}
activateView(el) {
/**
* Gets called from StackItem to initialize a new view
*/
if (!this.state.direction) {
const leavingEl = (this.leavingEl.current != null) ? this.leavingEl.current : undefined;
this.transitionView(el, leavingEl);
}
}
transitionView(enteringEl, leavingEl) {
//
/**
* Super hacky workaround to make sure containerEL is available
* since activateView might be called from StackItem before IonRouterOutlet is mounted
*/
if (this.containerEl && this.containerEl.current && this.containerEl.current.componentOnReady) {
this.commitView(enteringEl, leavingEl);
}
else {
setTimeout(() => {
this.transitionView(enteringEl, leavingEl);
}, 10);
}
}
async commitView(enteringEl, leavingEl) {
if (!this.inTransition) {
this.inTransition = true;
await this.containerEl.current.componentOnReady();
await this.containerEl.current.commit(enteringEl, leavingEl, {
deepWait: true,
duration: this.state.direction === undefined ? 0 : undefined,
direction: this.state.direction,
showGoBack: this.state.direction === 'forward',
progressAnimation: false
});
if (leavingEl) {
/**
* add hidden attributes
*/
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
}
this.inTransition = false;
}
}
componentDidUpdate(_prevProps, prevState) {
/**
* Don't transition the view if the state didn't change
* Probably means we are still on the same view
*/
if (prevState !== this.state) {
const enteringEl = (this.enteringEl.current != null) ? this.enteringEl.current : undefined;
const leavingEl = (this.leavingEl.current != null) ? this.leavingEl.current : undefined;
this.transitionView(enteringEl, leavingEl);
}
}
render() {
return (React.createElement(IonRouterOutletInner, { ref: this.containerEl },
React.createElement(NavContext.Provider, { value: { goBack: this.goBack } }, this.state.views.map((item) => {
return (React.createElement(NavContext.Consumer, null, context => {
this.context = context;
const viewStack = context.viewStacks[this.id];
const activeId = viewStack ? viewStack.activeId : '';
const views = (viewStack || { views: [] }).views.filter(x => x.show);
return (React.createElement(IonRouterOutletInner, { "data-id": this.id, ref: this.containerEl }, views.map((item) => {
let props = {};
if (item.id === this.state.prevActiveId) {
if (item.id === activeId) {
props = {
'ref': this.leavingEl
'className': ' ion-page-invisible'
};
}
else if (item.id === this.state.activeId) {
props = {
'ref': this.enteringEl,
'className': (this.state.direction != null ? ' ion-page-invisible' : '')
};
}
else {

@@ -816,8 +641,9 @@ props = {

}
return (React.createElement(StackItemManager, { key: item.id, mount: item.mount },
React.createElement(StackItem, Object.assign({ activateView: this.activateView }, props), this.renderChild(item))));
}))));
return (React.createElement(ViewItemManager, { id: item.id, key: item.key, mount: item.mount },
React.createElement(View, Object.assign({ ref: item.ref }, props), this.renderChild(item))));
})));
}));
}
}
const IonRouterOutlet = /*@__PURE__*/ withRouter(RouterOutlet);
const IonRouterOutlet = /*@__PURE__*/ withRouter(IonRouterOutletUnWrapped);

@@ -877,5 +703,222 @@ const hostStyles = {

addIcons(ICON_PATHS);
class IonRouter extends React.Component {
constructor(props) {
super(props);
this.goBack = (defaultHref) => {
const { view: leavingView } = this.findViewInfoByLocation(this.props.location, this.state.viewStacks);
if (leavingView) {
const { view: enteringView } = this.findViewInfoById(leavingView.prevId, this.state.viewStacks);
if (enteringView) {
this.props.history.replace(enteringView.match.url, { direction: 'back' });
}
else {
this.props.history.replace(defaultHref, { direction: 'back' });
}
}
else {
this.props.history.replace(defaultHref, { direction: 'back' });
}
};
this.state = {
viewStacks: {},
hideView: this.hideView.bind(this),
registerViewStack: this.registerView.bind(this),
removeViewStack: this.removeViewStack.bind(this),
goBack: this.goBack.bind(this),
transitionView: this.transitionView.bind(this)
};
}
componentWillMount() {
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
}
hideView(viewId) {
const viewStacks = Object.assign({}, this.state.viewStacks);
const { view } = this.findViewInfoById(viewId, viewStacks);
if (view) {
view.show = false;
view.key = generateUniqueId();
this.setState({
viewStacks
});
}
}
historyChange(location, action) {
this.setActiveView(location, action);
}
findViewInfoByLocation(location, viewStacks) {
let view;
let match;
let viewStack;
const keys = Object.keys(viewStacks);
keys.some(key => {
const vs = viewStacks[key];
return vs.views.some(x => {
match = matchPath$1(location.pathname, x.childProps);
if (match) {
view = x;
viewStack = vs;
return true;
}
return false;
});
});
const result = { view, viewStack, match };
return result;
}
findViewInfoById(id, viewStacks) {
let view;
let viewStack;
const keys = Object.keys(viewStacks);
keys.some(key => {
const vs = viewStacks[key];
view = vs.views.find(x => x.id === id);
if (view) {
viewStack = vs;
return true;
}
else {
return false;
}
});
return { view, viewStack };
}
setActiveView(location, action) {
const viewStacks = Object.assign({}, this.state.viewStacks);
const { view: enteringView, viewStack: enteringViewStack, match } = this.findViewInfoByLocation(location, viewStacks);
let direction = location.state && location.state.direction;
if (!enteringViewStack) {
return;
}
const { view: leavingView } = this.findViewInfoById(this.activeViewId, viewStacks);
if (leavingView && leavingView.match.url === location.pathname) {
return;
}
if (enteringView) {
/**
* 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.
*/
if (!enteringView.show && action === 'PUSH') {
enteringView.prevId = leavingView && leavingView.id;
}
enteringView.show = true;
enteringView.mount = true;
enteringView.match = match;
enteringViewStack.activeId = enteringView.id;
this.activeViewId = enteringView.id;
if (leavingView) {
this.prevViewId = leavingView.id;
if (leavingView.match.params.tab === enteringView.match.params.tab) {
if (action === 'PUSH') {
direction = direction || 'forward';
}
else {
direction = direction || 'back';
leavingView.mount = false;
}
}
/**
* Attempt to determine if the leaving view is a route redirect.
* If it is, take it out of the rendering phase.
* We assume Routes with render props are redirects, because of this users should not use
* the render prop for non redirects, and instead provide a component in its place.
*/
if (leavingView.element.type === Redirect || leavingView.element.props.render) {
leavingView.mount = false;
leavingView.show = false;
}
}
this.setState({
viewStacks
}, () => {
const enteringEl = enteringView.ref && enteringView.ref.current ? enteringView.ref.current : undefined;
const leavingEl = leavingView && leavingView.ref && leavingView.ref.current ? leavingView.ref.current : undefined;
this.transitionView(enteringEl, leavingEl, enteringViewStack.routerOutlet, direction);
});
}
}
componentWillUnmount() {
this.listenUnregisterCallback();
}
registerView(stack, activeId, stackItems, routerOutlet, location) {
this.setState((prevState) => {
const prevViewStacks = Object.assign({}, prevState.viewStacks);
prevViewStacks[stack] = {
activeId: activeId,
views: stackItems,
routerOutlet
};
return {
viewStacks: prevViewStacks
};
}, () => {
const { view: activeView } = this.findViewInfoById(activeId, this.state.viewStacks);
if (activeView) {
this.prevViewId = this.activeViewId;
this.activeViewId = activeView.id;
const direction = location.state && location.state.direction;
const { view: prevView } = this.findViewInfoById(this.prevViewId, this.state.viewStacks);
this.transitionView(activeView.ref.current, prevView && prevView.ref.current || undefined, routerOutlet, direction);
}
});
}
;
removeViewStack(stack) {
const viewStacks = Object.assign({}, this.state.viewStacks);
delete viewStacks[stack];
this.setState({
viewStacks
});
}
findActiveView(views) {
let view;
views.some(x => {
const match = matchPath$1(this.props.location.pathname, x.childProps);
if (match) {
view = x;
return true;
}
return false;
});
return view;
}
transitionView(enteringEl, leavingEl, ionRouterOuter, direction) {
/**
* Super hacky workaround to make sure ionRouterOutlet is available
* since transitionView might be called before IonRouterOutlet is fully mounted
*/
if (ionRouterOuter && ionRouterOuter.componentOnReady) {
this.commitView(enteringEl, leavingEl, ionRouterOuter, direction);
}
else {
setTimeout(() => {
this.transitionView(enteringEl, leavingEl, ionRouterOuter, direction);
}, 10);
}
}
async commitView(enteringEl, leavingEl, ionRouterOuter, direction) {
await ionRouterOuter.componentOnReady();
await ionRouterOuter.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined ? 0 : undefined,
direction: direction,
showGoBack: direction === 'forward',
progressAnimation: false
});
if (leavingEl && (enteringEl !== leavingEl)) {
/**
* add hidden attributes
*/
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
}
}
render() {
return (React.createElement(NavContext.Provider, { value: this.state }, this.props.children));
}
}
const IonRouterWrapped = withRouter$1(IonRouter);
defineCustomElements(window);
export { IonActionSheet, IonAlert, IonAnchor, IonApp, IonAvatar, IonBackButton, IonBackButtonInner, IonBackdrop, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCheckbox, IonChip, IonCol, IonContent, IonDatetime, IonFab, IonFabButton, IonFabList, IonFooter, IonGrid, IonHeader, IonIcon, IonImg, IonInfiniteScroll, IonInput, IonItem, IonItemDivider, IonItemGroup, IonItemOption, IonItemOptions, IonItemSliding, IonLabel, IonLifeCycleContext, IonList, IonListHeader, IonLoading, IonMenu, IonMenuButton, IonMenuToggle, IonModal, IonNav, IonNote, IonPage, IonPicker, IonPickerColumn, IonPopover, IonProgressBar, IonRadio, IonRadioGroup, IonRange, IonRefresher, IonRefresherContent, IonReorder, IonReorderGroup, IonRippleEffect, IonRouterOutlet, IonRouterOutletInner, IonRow, IonSearchbar, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonSelectPopover, IonSkeletonText, IonSlide, IonSlides, IonSpinner, IonSplitPane, IonTab, IonTabBar, IonTabBarInner, IonTabButton, IonTabs, IonText, IonTextarea, IonThumbnail, IonTitle, IonToast, IonToggle, IonToolbar, IonVirtualScroll, useIonViewDidEnter, useIonViewDidLeave, useIonViewWillEnter, useIonViewWillLeave, withIonLifeCycle };
export { IonActionSheet, IonAlert, IonAnchor, IonApp, IonAvatar, IonBackButton, IonBackButtonInner, IonBackdrop, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCheckbox, IonChip, IonCol, IonContent, IonDatetime, IonFab, IonFabButton, IonFabList, IonFooter, IonGrid, IonHeader, IonIcon, IonImg, IonInfiniteScroll, IonInput, IonItem, IonItemDivider, IonItemGroup, IonItemOption, IonItemOptions, IonItemSliding, IonLabel, IonLifeCycleContext, IonList, IonListHeader, IonLoading, IonMenu, IonMenuButton, IonMenuToggle, IonModal, IonNav, IonNote, IonPage, IonPicker, IonPickerColumn, IonPopover, IonProgressBar, IonRadio, IonRadioGroup, IonRange, IonRefresher, IonRefresherContent, IonReorder, IonReorderGroup, IonRippleEffect, IonRouterWrapped as IonRouter, IonRouterOutlet, IonRouterOutletInner, IonRow, IonSearchbar, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonSelectPopover, IonSkeletonText, IonSlide, IonSlides, IonSpinner, IonSplitPane, IonTab, IonTabBar, IonTabBarInner, IonTabButton, IonTabs, IonText, IonTextarea, IonThumbnail, IonTitle, IonToast, IonToggle, IonToolbar, IonVirtualScroll, useIonViewDidEnter, useIonViewDidLeave, useIonViewWillEnter, useIonViewWillLeave, withIonLifeCycle };

@@ -7,4 +7,2 @@ 'use strict';

var ionicons = require('ionicons');
var icons = require('ionicons/icons');
var loader = require('@ionic/core/loader');

@@ -16,2 +14,3 @@ var tslib_1 = require('tslib');

var reactRouter = require('react-router');
var reactRouterDom = require('react-router-dom');

@@ -414,3 +413,3 @@ function attachEventProps(node, newProps, oldProps = {}) {

class StackItemInternal extends React.Component {
class ViewInternal extends React.Component {
constructor(props) {

@@ -423,3 +422,3 @@ super(props);

componentDidMount() {
const { forwardedRef, activateView } = this.props;
const { forwardedRef } = this.props;
this.setState({ ref: forwardedRef });

@@ -431,5 +430,2 @@ if (forwardedRef && forwardedRef.current) {

forwardedRef.current.addEventListener('ionViewDidLeave', this.ionViewDidLeaveHandler.bind(this));
if (activateView) {
activateView(forwardedRef.current);
}
}

@@ -459,3 +455,3 @@ }

render() {
const _a = this.props, { className, children, forwardedRef, activateView } = _a, rest = tslib_1.__rest(_a, ["className", "children", "forwardedRef", "activateView"]);
const _a = this.props, { className, children, forwardedRef } = _a, rest = tslib_1.__rest(_a, ["className", "children", "forwardedRef"]);
const { ref } = this.state;

@@ -465,12 +461,20 @@ return (React.createElement("div", Object.assign({ className: className ? `ion-page ${className}` : 'ion-page', ref: forwardedRef }, rest), ref && children));

}
StackItemInternal.contextType = IonLifeCycleContext;
ViewInternal.contextType = IonLifeCycleContext;
function forwardRef$1(props, ref) {
return React.createElement(StackItemInternal, Object.assign({ forwardedRef: ref }, props));
return React.createElement(ViewInternal, Object.assign({ forwardedRef: ref }, props));
}
forwardRef$1.displayName = 'StackItem';
const StackItem = /*@__PURE__*/ React.forwardRef(forwardRef$1);
forwardRef$1.displayName = 'View';
const View = /*@__PURE__*/ React.forwardRef(forwardRef$1);
const NavContext = /*@__PURE__*/ React.createContext({
goBack: () => { }
viewStacks: {},
hideView: () => { navContextNotFoundError(); },
goBack: () => { navContextNotFoundError(); },
registerViewStack: () => { navContextNotFoundError(); },
removeViewStack: () => { navContextNotFoundError(); },
transitionView: () => { navContextNotFoundError(); }
});
function navContextNotFoundError() {
console.error('IonRouter not found, did you add it to the app?');
}

@@ -531,3 +535,3 @@ const withIonLifeCycle = (WrappedComponent) => {

class StackItemManager extends React.Component {
class ViewItemManager extends React.Component {
constructor(props) {

@@ -542,13 +546,9 @@ super(props);

if (!this.props.mount) {
/**
* Give child component time to finish calling its
* own onViewDidLeave before destroying it
*/
setTimeout(() => {
if (this._isMounted) {
this.setState({
show: false
});
}
}, 1000);
if (this._isMounted) {
this.setState({
show: false
}, () => {
this.context.hideView(this.props.id);
});
}
}

@@ -569,172 +569,55 @@ });

// TODO: treeshake
StackItemManager.contextType = IonLifeCycleContext;
ViewItemManager.contextType = NavContext;
class RouterOutlet extends React.Component {
class IonRouterOutletUnWrapped extends React.Component {
constructor(props) {
super(props);
this.enteringEl = React.createRef();
this.leavingEl = React.createRef();
this.containerEl = React.createRef();
this.inTransition = false;
this.goBack = (defaultHref) => {
const prevView = this.state.views.find(v => v.id === this.state.activeId);
const newView = this.state.views.find(v => v.id === prevView.prevId);
if (newView) {
this.props.history.replace(newView.location.pathname || defaultHref);
}
else {
this.id = this.props.id || generateUniqueId();
}
componentDidMount() {
const views = [];
let activeId;
React.Children.forEach(this.props.children, (child) => {
if (child.type === reactRouter.Switch) {
/**
* find the parent view based on the defaultHref and add it
* to the views collection so that navigation works properly
* If the first child is a Switch, loop through its children to build the viewStack
*/
let element;
let match;
React.Children.forEach(this.props.children, (child) => {
if (match == null) {
element = child;
match = reactRouter.matchPath(defaultHref, child.props);
}
React.Children.forEach(child.props.children, (grandChild) => {
addView.call(this, grandChild);
});
if (element && match) {
const viewId = generateUniqueId();
const parentView = {
id: viewId,
location: {
pathname: defaultHref
},
element: element,
match: match,
prevId: undefined,
mount: true
};
prevView.prevId = viewId;
this.setState({
views: [parentView, prevView]
});
}
this.props.history.replace(defaultHref);
}
};
this.state = {
direction: undefined,
activeId: undefined,
prevActiveId: undefined,
tabActiveIds: {},
views: []
};
this.activateView = this.activateView.bind(this);
}
static getDerivedStateFromProps(props, state) {
const location = props.location;
let match = null;
let element;
/**
* Remove any views that have been unmounted previously
*/
const views = state.views.filter(x => x.mount === true);
/**
* Get the current active view and if the path is the same then do nothing
*/
const activeView = views.find(v => v.id === state.activeId);
/**
* Look at all available paths and find the one that matches
*/
React.Children.forEach(props.children, (child) => {
if (match == null) {
element = child;
match = reactRouter.matchPath(location.pathname, child.props);
else {
addView.call(this, child);
}
});
/**
* If there are no matches then set the active view to null and exit
*/
if (!match) {
return {
direction: undefined,
activeId: undefined,
prevActiveId: undefined
this.context.registerViewStack(this.id, activeId, views, this.containerEl.current, this.props.location);
function addView(child) {
const location = this.props.history.location;
const viewId = generateUniqueId();
const key = generateUniqueId();
const element = child;
const match = reactRouter.matchPath(location.pathname, child.props);
const view = {
id: viewId,
key,
match,
element,
mount: true,
show: !!match,
ref: React.createRef(),
childProps: child.props
};
}
/**
* Get the active view for the tab that matches.
* If the location matches the existing tab path then set that view as active
*/
const id = state.tabActiveIds[match.params.tab];
const currentActiveTabView = views.find(v => v.id === id);
if (currentActiveTabView && currentActiveTabView.location.pathname === props.location.pathname) {
if (currentActiveTabView.id === state.activeId) {
/**
* The current tab was clicked, so do nothing
*/
return null;
if (!!match) {
activeId = viewId;
}
/**
* Activate a tab that is already in views
*/
return {
direction: undefined,
activeId: currentActiveTabView.id,
prevActiveId: state.activeId,
views: views
};
views.push(view);
return activeId;
}
/**
* If the new active view is a previous view, then animate it back in
*/
if (activeView) {
const prevActiveView = views.find(v => v.id === activeView.prevId);
if (prevActiveView && activeView.match.params.tab === match.params.tab && prevActiveView.match.url === match.url) {
return {
direction: 'back',
activeId: prevActiveView.id,
prevActiveId: activeView.id,
tabActiveIds: Object.assign({}, state.tabActiveIds, { [match.params.tab]: prevActiveView.id }),
views: views.map(x => {
if (x.id === activeView.id) {
return Object.assign({}, x, { mount: false });
}
return x;
})
};
}
}
/**
* If the current view does not match the url, see if the view that matches the url is currently in the stack.
* If so, show the view that matches the url and remove the current view.
*/
if (currentActiveTabView && currentActiveTabView.location.pathname !== props.location.pathname) {
const view = views.find(x => x.location.pathname == props.location.pathname);
if (view && view.id === currentActiveTabView.prevId) {
return {
direction: undefined,
activeId: view.id,
prevActiveId: undefined,
views: views.filter(x => x.id !== currentActiveTabView.id),
tabActiveIds: Object.assign({}, state.tabActiveIds, { [match.params.tab]: view.id }),
};
}
}
/**
* Else add this new view to the stack
*/
const viewId = generateUniqueId();
const newState = {
direction: (state.tabActiveIds[match.params.tab]) ? 'forward' : undefined,
activeId: viewId,
prevActiveId: state.tabActiveIds[match.params.tab] || state.activeId,
tabActiveIds: Object.assign({}, state.tabActiveIds, { [match.params.tab]: viewId }),
views: views.concat({
id: viewId,
location,
match,
element,
prevId: state.tabActiveIds[match.params.tab],
mount: true
})
};
return newState;
}
componentWillUnmount() {
this.context.removeViewStack(this.id);
}
renderChild(item) {
const component = React.cloneElement(item.element, {
location: item.location,
computedMatch: item.match

@@ -744,73 +627,15 @@ });

}
activateView(el) {
/**
* Gets called from StackItem to initialize a new view
*/
if (!this.state.direction) {
const leavingEl = (this.leavingEl.current != null) ? this.leavingEl.current : undefined;
this.transitionView(el, leavingEl);
}
}
transitionView(enteringEl, leavingEl) {
//
/**
* Super hacky workaround to make sure containerEL is available
* since activateView might be called from StackItem before IonRouterOutlet is mounted
*/
if (this.containerEl && this.containerEl.current && this.containerEl.current.componentOnReady) {
this.commitView(enteringEl, leavingEl);
}
else {
setTimeout(() => {
this.transitionView(enteringEl, leavingEl);
}, 10);
}
}
async commitView(enteringEl, leavingEl) {
if (!this.inTransition) {
this.inTransition = true;
await this.containerEl.current.componentOnReady();
await this.containerEl.current.commit(enteringEl, leavingEl, {
deepWait: true,
duration: this.state.direction === undefined ? 0 : undefined,
direction: this.state.direction,
showGoBack: this.state.direction === 'forward',
progressAnimation: false
});
if (leavingEl) {
/**
* add hidden attributes
*/
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
}
this.inTransition = false;
}
}
componentDidUpdate(_prevProps, prevState) {
/**
* Don't transition the view if the state didn't change
* Probably means we are still on the same view
*/
if (prevState !== this.state) {
const enteringEl = (this.enteringEl.current != null) ? this.enteringEl.current : undefined;
const leavingEl = (this.leavingEl.current != null) ? this.leavingEl.current : undefined;
this.transitionView(enteringEl, leavingEl);
}
}
render() {
return (React.createElement(IonRouterOutletInner, { ref: this.containerEl },
React.createElement(NavContext.Provider, { value: { goBack: this.goBack } }, this.state.views.map((item) => {
return (React.createElement(NavContext.Consumer, null, context => {
this.context = context;
const viewStack = context.viewStacks[this.id];
const activeId = viewStack ? viewStack.activeId : '';
const views = (viewStack || { views: [] }).views.filter(x => x.show);
return (React.createElement(IonRouterOutletInner, { "data-id": this.id, ref: this.containerEl }, views.map((item) => {
let props = {};
if (item.id === this.state.prevActiveId) {
if (item.id === activeId) {
props = {
'ref': this.leavingEl
'className': ' ion-page-invisible'
};
}
else if (item.id === this.state.activeId) {
props = {
'ref': this.enteringEl,
'className': (this.state.direction != null ? ' ion-page-invisible' : '')
};
}
else {

@@ -822,8 +647,9 @@ props = {

}
return (React.createElement(StackItemManager, { key: item.id, mount: item.mount },
React.createElement(StackItem, Object.assign({ activateView: this.activateView }, props), this.renderChild(item))));
}))));
return (React.createElement(ViewItemManager, { id: item.id, key: item.key, mount: item.mount },
React.createElement(View, Object.assign({ ref: item.ref }, props), this.renderChild(item))));
})));
}));
}
}
const IonRouterOutlet = /*@__PURE__*/ reactRouter.withRouter(RouterOutlet);
const IonRouterOutlet = /*@__PURE__*/ reactRouter.withRouter(IonRouterOutletUnWrapped);

@@ -883,3 +709,220 @@ const hostStyles = {

ionicons.addIcons(icons.ICON_PATHS);
class IonRouter extends React.Component {
constructor(props) {
super(props);
this.goBack = (defaultHref) => {
const { view: leavingView } = this.findViewInfoByLocation(this.props.location, this.state.viewStacks);
if (leavingView) {
const { view: enteringView } = this.findViewInfoById(leavingView.prevId, this.state.viewStacks);
if (enteringView) {
this.props.history.replace(enteringView.match.url, { direction: 'back' });
}
else {
this.props.history.replace(defaultHref, { direction: 'back' });
}
}
else {
this.props.history.replace(defaultHref, { direction: 'back' });
}
};
this.state = {
viewStacks: {},
hideView: this.hideView.bind(this),
registerViewStack: this.registerView.bind(this),
removeViewStack: this.removeViewStack.bind(this),
goBack: this.goBack.bind(this),
transitionView: this.transitionView.bind(this)
};
}
componentWillMount() {
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
}
hideView(viewId) {
const viewStacks = Object.assign({}, this.state.viewStacks);
const { view } = this.findViewInfoById(viewId, viewStacks);
if (view) {
view.show = false;
view.key = generateUniqueId();
this.setState({
viewStacks
});
}
}
historyChange(location, action) {
this.setActiveView(location, action);
}
findViewInfoByLocation(location, viewStacks) {
let view;
let match;
let viewStack;
const keys = Object.keys(viewStacks);
keys.some(key => {
const vs = viewStacks[key];
return vs.views.some(x => {
match = reactRouterDom.matchPath(location.pathname, x.childProps);
if (match) {
view = x;
viewStack = vs;
return true;
}
return false;
});
});
const result = { view, viewStack, match };
return result;
}
findViewInfoById(id, viewStacks) {
let view;
let viewStack;
const keys = Object.keys(viewStacks);
keys.some(key => {
const vs = viewStacks[key];
view = vs.views.find(x => x.id === id);
if (view) {
viewStack = vs;
return true;
}
else {
return false;
}
});
return { view, viewStack };
}
setActiveView(location, action) {
const viewStacks = Object.assign({}, this.state.viewStacks);
const { view: enteringView, viewStack: enteringViewStack, match } = this.findViewInfoByLocation(location, viewStacks);
let direction = location.state && location.state.direction;
if (!enteringViewStack) {
return;
}
const { view: leavingView } = this.findViewInfoById(this.activeViewId, viewStacks);
if (leavingView && leavingView.match.url === location.pathname) {
return;
}
if (enteringView) {
/**
* 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.
*/
if (!enteringView.show && action === 'PUSH') {
enteringView.prevId = leavingView && leavingView.id;
}
enteringView.show = true;
enteringView.mount = true;
enteringView.match = match;
enteringViewStack.activeId = enteringView.id;
this.activeViewId = enteringView.id;
if (leavingView) {
this.prevViewId = leavingView.id;
if (leavingView.match.params.tab === enteringView.match.params.tab) {
if (action === 'PUSH') {
direction = direction || 'forward';
}
else {
direction = direction || 'back';
leavingView.mount = false;
}
}
/**
* Attempt to determine if the leaving view is a route redirect.
* If it is, take it out of the rendering phase.
* We assume Routes with render props are redirects, because of this users should not use
* the render prop for non redirects, and instead provide a component in its place.
*/
if (leavingView.element.type === reactRouterDom.Redirect || leavingView.element.props.render) {
leavingView.mount = false;
leavingView.show = false;
}
}
this.setState({
viewStacks
}, () => {
const enteringEl = enteringView.ref && enteringView.ref.current ? enteringView.ref.current : undefined;
const leavingEl = leavingView && leavingView.ref && leavingView.ref.current ? leavingView.ref.current : undefined;
this.transitionView(enteringEl, leavingEl, enteringViewStack.routerOutlet, direction);
});
}
}
componentWillUnmount() {
this.listenUnregisterCallback();
}
registerView(stack, activeId, stackItems, routerOutlet, location) {
this.setState((prevState) => {
const prevViewStacks = Object.assign({}, prevState.viewStacks);
prevViewStacks[stack] = {
activeId: activeId,
views: stackItems,
routerOutlet
};
return {
viewStacks: prevViewStacks
};
}, () => {
const { view: activeView } = this.findViewInfoById(activeId, this.state.viewStacks);
if (activeView) {
this.prevViewId = this.activeViewId;
this.activeViewId = activeView.id;
const direction = location.state && location.state.direction;
const { view: prevView } = this.findViewInfoById(this.prevViewId, this.state.viewStacks);
this.transitionView(activeView.ref.current, prevView && prevView.ref.current || undefined, routerOutlet, direction);
}
});
}
;
removeViewStack(stack) {
const viewStacks = Object.assign({}, this.state.viewStacks);
delete viewStacks[stack];
this.setState({
viewStacks
});
}
findActiveView(views) {
let view;
views.some(x => {
const match = reactRouterDom.matchPath(this.props.location.pathname, x.childProps);
if (match) {
view = x;
return true;
}
return false;
});
return view;
}
transitionView(enteringEl, leavingEl, ionRouterOuter, direction) {
/**
* Super hacky workaround to make sure ionRouterOutlet is available
* since transitionView might be called before IonRouterOutlet is fully mounted
*/
if (ionRouterOuter && ionRouterOuter.componentOnReady) {
this.commitView(enteringEl, leavingEl, ionRouterOuter, direction);
}
else {
setTimeout(() => {
this.transitionView(enteringEl, leavingEl, ionRouterOuter, direction);
}, 10);
}
}
async commitView(enteringEl, leavingEl, ionRouterOuter, direction) {
await ionRouterOuter.componentOnReady();
await ionRouterOuter.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined ? 0 : undefined,
direction: direction,
showGoBack: direction === 'forward',
progressAnimation: false
});
if (leavingEl && (enteringEl !== leavingEl)) {
/**
* add hidden attributes
*/
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
}
}
render() {
return (React.createElement(NavContext.Provider, { value: this.state }, this.props.children));
}
}
const IonRouterWrapped = reactRouterDom.withRouter(IonRouter);
loader.defineCustomElements(window);

@@ -954,2 +997,3 @@

exports.IonRippleEffect = IonRippleEffect;
exports.IonRouter = IonRouterWrapped;
exports.IonRouterOutlet = IonRouterOutlet;

@@ -956,0 +1000,0 @@ exports.IonRouterOutletInner = IonRouterOutletInner;

@@ -14,1 +14,2 @@ export { AlertButton, AlertInput } from '@ionic/core';

export { IonBackButton } from './navigation/IonBackButton';
export { IonRouterWrapped as IonRouter } from './navigation/IonRouter';
import React from 'react';
import { RouteComponentProps, match, RouteProps } from 'react-router';
import { RouteComponentProps, RouteProps, match } from 'react-router';
declare type ChildProps = RouteProps & {

@@ -7,5 +7,6 @@ computedMatch: match<any>;

declare type IonRouterOutletProps = RouteComponentProps & {
id?: string;
children?: React.ReactElement<ChildProps>[] | React.ReactElement<ChildProps>;
};
export declare const IonRouterOutlet: React.ComponentClass<Pick<IonRouterOutletProps, "children">, any>;
export declare const IonRouterOutlet: React.ComponentClass<Pick<IonRouterOutletProps, "id" | "children">, any>;
export {};
import React from 'react';
interface NavContextInterface {
import { ViewItem } from './ViewItem';
import { NavDirection } from '@ionic/core';
import { Location } from 'history';
export interface ViewStack {
routerOutlet: HTMLIonRouterOutletElement;
activeId?: string;
views: ViewItem[];
}
export interface ViewStacks {
[key: string]: ViewStack;
}
export interface NavContextState {
hideView: (viewId: string) => void;
viewStacks: ViewStacks;
registerViewStack: (stack: string, activeId: string, stackItems: ViewItem[], ionRouterOutlet: HTMLIonRouterOutletElement, location: Location) => void;
removeViewStack: (stack: string) => void;
goBack: (defaultHref?: string) => void;
transitionView: (enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) => void;
}
export declare const NavContext: React.Context<NavContextInterface>;
export {};
export declare const NavContext: React.Context<NavContextState>;
{
"name": "@ionic/react",
"version": "0.0.6-3",
"version": "0.0.6-4",
"description": "React specific wrapper for @ionic/core",

@@ -39,4 +39,4 @@ "keywords": [

"dependencies": {
"@ionic/core": "4.5.0-dev.201906101548.7af9cb2",
"tslib": "^1.9.3"
"@ionic/core": "^4.6.0-dev.201906192117.6727cfc",
"tslib": "^1.10.0"
},

@@ -67,2 +67,3 @@ "peerDependencies": {

"rollup-plugin-node-resolve": "^5.0.1",
"rollup-plugin-sourcemaps": "^0.4.2",
"ts-jest": "^24.0.2",

@@ -69,0 +70,0 @@ "typescript": "3.5.1"

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc