Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# @dev-plugins/react-navigation
# @bam.tech/react-navigation-visualizer-dev-plugin

A React Navigation DevTool that can run in an Expo App
A Visualization Tool based on Expo DevTools Plugin for React Navigation.

## Installation

### Add the package to your project

```bash
npx expo install @dev-plugins/react-navigation
npx expo install@bam.tech/react-navigation-visualizer-dev-plugin
```

## Usage
Expand All @@ -18,7 +18,7 @@ npx expo install @dev-plugins/react-navigation

```jsx
import { useNavigationContainerRef } from '@react-navigation/native';
import { useReactNavigationDevTools } from '@dev-plugins/react-navigation';
import { useReactNavigationDevTools } from '@bam.tech/react-navigation-visualizer-dev-plugin';

export default function App() {
const navigationRef = useNavigationContainerRef();
Expand All @@ -34,13 +34,13 @@ When using `expo-router`, integrate the DevTool in your main `_layout.tsx` file.

```tsx
import { useNavigationContainerRef } from 'expo-router';
import { useReactNavigationDevTools } from '@dev-plugins/react-navigation';
import { useReactNavigationDevTools } from '@bam.tech/react-navigation-visualizer-dev-plugin';

export default function RootLayout() {
const navigationRef = useNavigationContainerRef();
useReactNavigationDevTools(navigationRef);

return <Stack />
return <Stack />;
}
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@dev-plugins/react-navigation",
"version": "0.1.0",
"description": "Expo DevTools Plugin for React Navigation",
"name": "@bam.tech/react-navigation-visualizer-dev-plugin",
"version": "0.1.1",
"description": "Visualization Tool based on Expo DevTools Plugin for React Navigation",
"main": "build/index.js",
"types": "build/index.d.ts",
"sideEffects": false,
Expand All @@ -10,7 +10,7 @@
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"prepare": "expo-module prepare && node ../../scripts/build-webui.js react-navigation",
"prepare": "expo-module prepare && node ../../scripts/build-webui.js react-navigation-visualizer",
"prepublishOnly": "expo-module prepublishOnly",
"expo-module": "expo-module"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Layout, Tabs, theme as antTheme, ThemeConfig } from 'antd';
import { ThemeProvider } from '@emotion/react';
import styled from '@emotion/styled';
import { theme as antTheme, Layout, Tabs, ThemeConfig } from 'antd';
import * as React from 'react';

import { theme } from './theme';

import { LinkingTester } from './LinkingTester';
import { Logs } from './Logs';
import { NavigationTree } from './NavigationTree';
import { theme } from './theme';
import { usePluginStore } from './usePluginStore';

declare module '@emotion/react' {
Expand All @@ -31,6 +31,9 @@ export default function App() {
<TabsContent tab={<TabLabel>Linking</TabLabel>} key="linking">
<LinkingTester active={activeKey === 'linking'} {...store} />
</TabsContent>
<TabsContent tab={<TabLabel>Stack Visualization</TabLabel>} key="navigationTree">
<NavigationTree {...store} />
</TabsContent>
</Tabs>
</Container>
</ThemeProvider>
Expand Down
133 changes: 133 additions & 0 deletions packages/react-navigation-visualizer/webui/src/NavigationTree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import styled from '@emotion/styled';
import { Layout, Typography } from 'antd';
import * as React from 'react';

import { Sidebar } from './Sidebar';
import type { NavigationRoute, StoreType } from './types';

type Props = StoreType;

export function NavigationTree({ logs }: Props) {
const currentNavigationItem = logs[logs.length - 1];
const previousNavigationItem = logs[logs.length - 2];

const hasCurrentItem = !!currentNavigationItem;
const hasPreviousItem = !!previousNavigationItem;

return (
<Layout style={{ height: '100%' }}>
<Layout.Content style={{ height: '100%' }}>
<Container>
<HalfContainer>
<Typography>Previous state</Typography>
<HalfContent>
{hasPreviousItem && (
<Node name="root" routes={previousNavigationItem.state?.routes} />
)}
</HalfContent>
</HalfContainer>
<HalfContainer>
<Typography>Current state</Typography>
<HalfContent>
{hasCurrentItem && <Node name="root" routes={currentNavigationItem.state?.routes} />}
</HalfContent>
</HalfContainer>
</Container>
</Layout.Content>
{hasCurrentItem ? (
<Sidebar
action={currentNavigationItem.action}
state={currentNavigationItem.state}
stack={currentNavigationItem.stack}
/>
) : null}
</Layout>
);
}

const Container = styled.div({
display: 'flex',
overflow: 'auto',
flexDirection: 'row',
height: 'calc(100vh - 80px)',
flex: 1,
});

const HalfContainer = styled.div({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
overflow: 'auto',
justifyContent: 'space-between',
height: '100%',
flex: 1,
});

const HalfContent = styled.div({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
overflow: 'auto',
justifyContent: 'flex-end',
height: '100%',
flex: 1,
});

const Spacer = styled.div({
height: 4,
});

const LeafContainer = styled.div(({ theme: antdTheme }) => ({
display: 'flex',
backgroundColor: antdTheme.token?.colorPrimary,
borderRadius: 4,
alignItems: 'center',
justifyContent: 'center',
padding: 8,
}));

const LeafTitle = styled(Typography)({
color: 'white',
});

const Leaf = ({ title }: { title: string }) => {
return (
<LeafContainer>
<LeafTitle>{title}</LeafTitle>
</LeafContainer>
);
};

const NodeContainer = styled.div(({ theme: antdTheme }) => ({
display: 'flex',
flexDirection: 'column',
borderRadius: 4,
borderWidth: 1,
border: 'solid',
borderColor: antdTheme.token?.colorPrimary,
padding: 8,
}));

const NodeTitle = styled(Typography)(({ theme }) => ({
color: theme.token?.colorPrimary,
alignSelf: 'flex-start',
}));

const Node = ({ name, routes }: { name: string; routes: NavigationRoute[] | undefined }) => {
if (!routes || !routes.length) {
return <Leaf title={name} />;
}

return (
<NodeContainer>
{routes.toReversed().map((route, index) => (
<React.Fragment key={index}>
<Node name={route.name} routes={route.state?.routes} />
<Spacer />
</React.Fragment>
))}
<Spacer />
<NodeTitle>{name}</NodeTitle>
</NodeContainer>
);
};