Skip to content

Commit 041ab12

Browse files
committed
feat: make stack collapsible (#9)
* chore: remove unecessary styling * feat: make stacks collapsible * refactor: add color prop to Node container * refactor: add color prop to Leaf Container * refactor: add color prop to Selected Leaf Container * refactor: add selected prop to leaf title * refactor: extract legend color * refactor: remove unuseful logs * feat: center collapsed stack title * feat: highlight hovered stacks * feat: update colors calculation
1 parent bc3f81a commit 041ab12

File tree

1 file changed

+91
-36
lines changed

1 file changed

+91
-36
lines changed

packages/react-navigation-visualizer/webui/src/NavigationTree.tsx

Lines changed: 91 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,30 @@ const Spacer = styled.div({
9090
width: 4,
9191
});
9292

93-
const LeafContainer = styled.div(({ theme: antdTheme }) => ({
93+
const LeafContainer = styled.div<{ color: string }>(({ color }) => ({
9494
display: 'flex',
9595
flex: 1,
96-
backgroundColor: antdTheme.token?.colorPrimary,
9796
borderRadius: 4,
97+
backgroundColor: color,
9898
alignItems: 'center',
9999
justifyContent: 'center',
100100
padding: 8,
101101
}));
102102

103-
const SelectedLeafContainer = styled.div({
103+
const SelectedLeafContainer = styled.div<{ color: string }>(({ color }) => ({
104104
display: 'flex',
105105
flex: 1,
106106
padding: 4,
107107
border: 'dashed',
108+
borderColor: color,
108109
borderRadius: 4,
109110
borderWidth: 2,
110-
});
111+
}));
111112

112-
const LeafTitle = styled(Typography.Text)({
113+
const LeafTitle = styled(Typography.Text)<{ isSelected?: boolean }>(({ isSelected }) => ({
113114
color: 'white',
114-
});
115+
textDecoration: isSelected ? 'underline' : 'none',
116+
}));
115117

116118
const Leaf = ({
117119
title,
@@ -124,33 +126,41 @@ const Leaf = ({
124126
}) => {
125127
const Wrapper = isSelectedTab ? SelectedLeafContainer : React.Fragment;
126128
return (
127-
<Wrapper style={{ borderColor: color }}>
128-
<LeafContainer style={{ backgroundColor: color }}>
129-
<LeafTitle style={{ textDecoration: isSelectedTab ? 'underline' : 'none' }}>
130-
{title}
131-
</LeafTitle>
129+
<Wrapper color={color}>
130+
<LeafContainer color={color}>
131+
<LeafTitle isSelected={isSelectedTab}>{title}</LeafTitle>
132132
</LeafContainer>
133133
</Wrapper>
134134
);
135135
};
136136

137-
const NodeContainer = styled.div(({ theme: antdTheme }) => ({
137+
const NodeContainer = styled.div<{ color: string; isClosed?: boolean }>(({ color, isClosed }) => ({
138138
display: 'flex',
139139
flexDirection: 'column',
140140
borderRadius: 4,
141141
borderWidth: 1,
142142
border: 'solid',
143-
borderColor: antdTheme.token?.colorPrimary,
144-
borderTopWidth: 0,
145-
borderTopLeftRadius: 0,
146-
borderTopRightRadius: 0,
143+
borderColor: color,
144+
borderTopWidth: isClosed ? 1 : 0,
145+
borderTopLeftRadius: isClosed ? 4 : 0,
146+
borderTopRightRadius: isClosed ? 4 : 0,
147147
padding: 8,
148148
paddingTop: 4,
149+
cursor: 'pointer',
150+
backgroundColor: 'transparent',
151+
':hover:not(:has(:hover))': {
152+
backgroundColor: `${color}30`,
153+
},
149154
}));
150155

151-
const NodeTitle = styled(Typography)(({ theme }) => ({
152-
color: theme.token?.colorPrimary,
156+
const NodeTitle = styled(Typography)<{ color: string }>(({ color }) => ({
153157
alignSelf: 'flex-start',
158+
color,
159+
}));
160+
161+
const ClosedNodeTitle = styled(Typography)<{ color: string }>(({ color }) => ({
162+
alignSelf: 'center',
163+
color,
154164
}));
155165

156166
const TabContainer = styled.div({
@@ -170,6 +180,8 @@ const Node = ({
170180
state: NavigationState;
171181
parentColor: string;
172182
}) => {
183+
const [isClosed, setIsClosed] = React.useState(false);
184+
173185
const routes = state.routes;
174186
if (!routes || !routes.length) {
175187
return <Leaf title={name} color={parentColor} />;
@@ -179,11 +191,20 @@ const Node = ({
179191

180192
const StackWrapper = state.type === 'tab' ? TabContainer : React.Fragment;
181193

194+
if (isClosed) {
195+
return <ClosedNode name={name} color={color} openNode={() => setIsClosed(false)} />;
196+
}
197+
182198
return (
183-
<NodeContainer style={{ borderColor: color }}>
199+
<NodeContainer
200+
color={color}
201+
onClick={(e) => {
202+
setIsClosed(true);
203+
e.stopPropagation();
204+
}}>
184205
<StackWrapper>
185206
{routes.toReversed().map((route, index) => (
186-
<React.Fragment key={index}>
207+
<React.Fragment key={route.key}>
187208
{route.state?.routes && route.state.routes.length ? (
188209
<Node name={route.name} state={route.state} parentColor={color} />
189210
) : (
@@ -199,50 +220,84 @@ const Node = ({
199220
</React.Fragment>
200221
))}
201222
</StackWrapper>
202-
<NodeTitle style={{ color }}>{name}</NodeTitle>
223+
<NodeTitle color={color}>{name}</NodeTitle>
224+
</NodeContainer>
225+
);
226+
};
227+
228+
const ClosedNode = ({
229+
name,
230+
color,
231+
openNode,
232+
}: {
233+
name: string;
234+
color: string;
235+
openNode: () => void;
236+
}) => {
237+
return (
238+
<NodeContainer
239+
color={color}
240+
style={{ borderColor: color }}
241+
onClick={(e) => {
242+
openNode();
243+
e.stopPropagation();
244+
}}
245+
isClosed>
246+
<ClosedNodeTitle color={color}>{name}</ClosedNodeTitle>
203247
</NodeContainer>
204248
);
205249
};
206250

207251
const colorMap: Record<string, string> = {};
208-
let currentHue = 0;
252+
let currentIndex = 0;
253+
254+
const colorList = [
255+
'#EF5350',
256+
'#EC407A',
257+
'#AB47BC',
258+
'#7E57C2',
259+
'#5C6BC0',
260+
'#42A5F5',
261+
'#29B6F6',
262+
'#26C6DA',
263+
'#26A69A',
264+
'#66BB6A',
265+
];
209266

210267
const generateColor = (key: string) => {
211268
if (colorMap[key]) {
212-
console.log(key);
213-
214269
return colorMap[key];
215270
}
216271

217-
currentHue = (currentHue + 15) % 360;
218-
const newColor = `hsl(${currentHue}, 70%, 50%)`;
272+
currentIndex = (currentIndex + 1) % colorList.length;
273+
const newColor = colorList[currentIndex];
219274

220275
colorMap[key] = newColor;
221276

222-
console.log(newColor);
223-
224277
return newColor;
225278
};
226279

280+
const legendRed = '#EF5350';
281+
227282
const Legend = () => {
228283
return (
229284
<div style={{ padding: 12 }}>
230-
<NodeContainer style={{ borderColor: 'hsl(0, 70%, 50%)' }}>
231-
<Leaf title="Screen" color="hsl(0, 70%, 50%)" />
285+
<NodeContainer color={legendRed}>
286+
<Leaf title="Screen" color={legendRed} />
232287
<div style={{ height: 4 }} />
233-
<NodeTitle style={{ color: 'hsl(0, 70%, 50%)' }}>Stack Navigator</NodeTitle>
288+
<NodeTitle color={legendRed}>Stack Navigator</NodeTitle>
234289
</NodeContainer>
235290
<div style={{ height: 12 }} />
236-
<NodeContainer style={{ borderColor: 'hsl(0, 70%, 50%)' }}>
291+
<NodeContainer color={legendRed}>
237292
<div style={{ display: 'flex', flexDirection: 'row' }}>
238-
<Leaf title="Unselected Tab" color="hsl(0, 70%, 50%)" />
293+
<Leaf title="Unselected Tab" color={legendRed} />
239294
<div style={{ width: 4 }} />
240-
<SelectedLeafContainer style={{ borderColor: 'hsl(0, 70%, 50%)' }}>
241-
<Leaf title="Selected Tab" color="hsl(0, 70%, 50%)" />
295+
<SelectedLeafContainer color={legendRed}>
296+
<Leaf title="Selected Tab" color={legendRed} />
242297
</SelectedLeafContainer>
243298
</div>
244299
<div style={{ height: 4 }} />
245-
<NodeTitle style={{ color: 'hsl(0, 70%, 50%)' }}>Tab Navigator</NodeTitle>
300+
<NodeTitle color={legendRed}>Tab Navigator</NodeTitle>
246301
</NodeContainer>
247302
</div>
248303
);

0 commit comments

Comments
 (0)