diff --git a/example/main.js b/example/main.js
index 5fb36cf..d1bdd1f 100644
--- a/example/main.js
+++ b/example/main.js
@@ -11,7 +11,11 @@ import {
NavigationProvider,
StackNavigation,
} from '@exponent/ex-navigation';
-import { Colors, ThemeProvider, Drawer } from 'react-native-paper';
+import {
+ Colors,
+ Drawer,
+ Provider as PaperProvider,
+} from 'react-native-paper';
import Router from './src/Router';
@@ -53,7 +57,7 @@ class App extends Component {
render() {
return (
-
+
-
+
);
}
}
diff --git a/src/components/Portal.js b/src/components/Portal/Portal.js
similarity index 63%
rename from src/components/Portal.js
rename to src/components/Portal/Portal.js
index 54be528..af202a9 100644
--- a/src/components/Portal.js
+++ b/src/components/Portal/Portal.js
@@ -4,14 +4,24 @@ import {
PureComponent,
PropTypes,
} from 'react';
-import { manager } from '../core/PortalHost';
+import { manager } from './PortalHost';
-type Props = {
- children?: any;
+export type PortalProps = {
+ children: any;
+ position: number;
}
+type Props = PortalProps;
+
+/**
+ * Portal allows to render a component at a different place in the parent tree.
+ */
export default class Portal extends PureComponent {
static propTypes = {
+ /**
+ * Position of the element in the z-axis
+ */
+ position: PropTypes.number,
children: PropTypes.node.isRequired,
};
@@ -26,11 +36,11 @@ export default class Portal extends PureComponent {
'You need to wrap your root component in \'\''
);
}
- this._key = this.context[manager].mount(this.props.children);
+ this._key = this.context[manager].mount(this.props);
}
componentDidUpdate() {
- this.context[manager].update(this._key, this.props.children);
+ this.context[manager].update(this._key, this.props);
}
componentWillUnmount() {
diff --git a/src/components/Portal/PortalHost.js b/src/components/Portal/PortalHost.js
new file mode 100644
index 0000000..7d2298b
--- /dev/null
+++ b/src/components/Portal/PortalHost.js
@@ -0,0 +1,115 @@
+/* @flow */
+
+import React, {
+ PureComponent,
+ PropTypes,
+} from 'react';
+import {
+ View,
+ StyleSheet,
+} from 'react-native';
+import type { PortalProps } from './Portal';
+
+type Props = {
+ children?: any;
+}
+
+type State = {
+ portals: Array<{
+ key: number;
+ props: PortalProps;
+ }>;
+}
+
+export const manager = 'react-native-paper$portal-manager';
+
+/**
+ * Portal host is the component which actually renders all Portals.
+ */
+export default class Portals extends PureComponent {
+ static propTypes = {
+ children: PropTypes.node.isRequired,
+ };
+
+ static childContextTypes = {
+ [manager]: PropTypes.object,
+ };
+
+ state = {
+ portals: [],
+ };
+
+ getChildContext() {
+ return {
+ [manager]: {
+ mount: this._mountPortal,
+ unmount: this._unmountPortal,
+ update: this._updatePortal,
+ },
+ };
+ }
+
+ _nextId = 0;
+
+ _mountPortal = (props: PortalProps) => {
+ const portals = this.state.portals;
+ this.setState({
+ portals: portals.concat({ key: this._nextId, props }),
+ });
+ return this._nextId++;
+ };
+
+ _unmountPortal = (key: number) => {
+ const portals = this.state.portals;
+ this.setState({
+ portals: portals.filter(item => item.key !== key),
+ });
+ };
+
+ _updatePortal = (key: number, props: PortalProps) => {
+ const portals = this.state.portals;
+ this.setState({
+ portals: portals.map(item => {
+ if (item.key === key) {
+ return { ...item, props };
+ }
+ return item;
+ }),
+ });
+ };
+
+ render() {
+ const { portals } = this.state;
+ return (
+
+ {this.props.children}
+ {portals
+ .reduce((acc, curr) => {
+ const { position = 0, children } = curr.props;
+ let group = acc.find(it => it.position === position);
+ if (group) {
+ group = {
+ position,
+ items: group.items.concat([ children ]),
+ };
+ return acc.map(g => {
+ if (group && g.position === position) {
+ return group;
+ }
+ return g;
+ });
+ } else {
+ group = { position, items: [ children ] };
+ return [ ...acc, group ];
+ }
+ }, [])
+ .map(({ position, items }) => (
+
+ {items}
+
+ ))
+ }
+
+ );
+ }
+}
diff --git a/src/components/Portal/ThemedPortal.js b/src/components/Portal/ThemedPortal.js
new file mode 100644
index 0000000..152f2f2
--- /dev/null
+++ b/src/components/Portal/ThemedPortal.js
@@ -0,0 +1,38 @@
+/* @flow */
+
+import React, {
+ Component,
+ Children,
+ PropTypes,
+} from 'react';
+import Portal from './Portal';
+import ThemeProvider from '../../core/ThemeProvider';
+import withTheme from '../../core/withTheme';
+import type { Theme } from '../../types/Theme';
+
+type Props = {
+ children?: any;
+ theme: Theme;
+}
+
+/**
+ * Themed portal is a special portal which preserves the theme in the context.
+ */
+class ThemedPortal extends Component {
+ static propTypes = {
+ children: PropTypes.element.isRequired,
+ theme: PropTypes.object.isRequired,
+ };
+
+ render() {
+ return (
+
+
+ {Children.only(this.props.children)}
+
+
+ );
+ }
+}
+
+export default withTheme(ThemedPortal);
diff --git a/src/core/PortalHost.js b/src/core/PortalHost.js
deleted file mode 100644
index 1c1027b..0000000
--- a/src/core/PortalHost.js
+++ /dev/null
@@ -1,111 +0,0 @@
-/* @flow */
-
-import React, {
- PureComponent,
- isValidElement,
- PropTypes,
-} from 'react';
-import {
- View,
- StyleSheet,
-} from 'react-native';
-
-type Props = {
- children?: any;
- style?: any;
-}
-
-type State = {
- portals: { [key: string]: ?React.Element<*> };
-}
-
-export const manager = 'react-native-paper$portal-manager';
-
-export default class PortalHost extends PureComponent {
- static propTypes = {
- children: PropTypes.node.isRequired,
- style: View.propTypes.style,
- };
-
- static childContextTypes = {
- [manager]: PropTypes.object,
- };
-
- state = {
- portals: {},
- };
-
- getChildContext() {
- return {
- [manager]: {
- mount: this._mountPortal,
- unmount: this._unmountPortal,
- update: this._updatePortal,
- },
- };
- }
-
- _nextId = 0;
-
- _mountPortal = (portal: ?React.Element<*>) => {
- const { portals } = this.state;
-
- if (isValidElement(portal)) {
- this.setState({
- portals: {
- ...portals,
- [this._nextId]: portal,
- },
- });
- return this._nextId++;
- }
-
- return null;
- };
-
- _unmountPortal = (key: string) => {
- let { portals } = this.state;
-
- if (portals.hasOwnProperty(key)) {
- portals = { ...portals };
- delete portals[key];
-
- this.setState({
- portals,
- });
- }
- };
-
- _updatePortal = (key: string, portal: ?React.Element<*>) => {
- const { portals } = this.state;
-
- if (isValidElement(portal)) {
- this.setState({
- portals: {
- ...portals,
- [key]: portal,
- },
- });
- }
- };
-
- render() {
- const { portals } = this.state;
- return (
-
- {this.props.children}
-
- {Object.keys(portals).map(key =>
- portals[key]
- )}
-
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- },
-});
diff --git a/src/core/Provider.js b/src/core/Provider.js
new file mode 100644
index 0000000..ce363dc
--- /dev/null
+++ b/src/core/Provider.js
@@ -0,0 +1,32 @@
+/* @flow */
+
+import React, {
+ PureComponent,
+ PropTypes,
+ Children,
+} from 'react';
+import ThemeProvider from './ThemeProvider';
+import PortalHost from '../components/Portal/PortalHost';
+import type { Theme } from '../types/Theme';
+
+type Props = {
+ children?: any;
+ theme?: Theme
+}
+
+export default class Provider extends PureComponent {
+ static propTypes = {
+ children: PropTypes.element.isRequired,
+ theme: PropTypes.object,
+ };
+
+ render() {
+ return (
+
+
+ {Children.only(this.props.children)}
+
+
+ );
+ }
+}
diff --git a/src/core/ThemeProvider.js b/src/core/ThemeProvider.js
index 4617f95..7a6c406 100644
--- a/src/core/ThemeProvider.js
+++ b/src/core/ThemeProvider.js
@@ -21,7 +21,7 @@ export const theme = 'react-native-paper$theme';
export default class ThemeProvider extends PureComponent {
static propTypes = {
- children: PropTypes.node.isRequired,
+ children: PropTypes.element.isRequired,
theme: PropTypes.object,
};
diff --git a/src/index.js b/src/index.js
index 2cc0fbb..890c436 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,7 +2,7 @@
export { default as withTheme } from './core/withTheme';
export { default as ThemeProvider } from './core/ThemeProvider';
-export { default as PortalHost } from './core/PortalHost';
+export { default as Provider } from './core/Provider';
export * as Colors from './styles/colors';