Skip to content

Commit ad88f5e

Browse files
committed
Add tabs UI to the Send page
1 parent 9979bf0 commit ad88f5e

File tree

3 files changed

+198
-22
lines changed

3 files changed

+198
-22
lines changed

src/components/send/send-page.tsx

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { SendStore } from '../../model/send/send-store';
99
import { ContainerSizedEditor } from '../editor/base-editor';
1010

1111
import { SplitPane } from '../split-pane';
12+
import { SendTabs, TAB_BAR_HEIGHT } from './send-tabs';
1213
import { RequestPane } from './request-pane';
1314
import { ResponsePane } from './response-pane';
1415

@@ -18,6 +19,12 @@ const SendPageContainer = styled.div`
1819
background-color: ${p => p.theme.mainBackground};
1920
`;
2021

22+
const TabContentContainer = styled.div`
23+
position: relative;
24+
height: calc(100vh - ${TAB_BAR_HEIGHT});
25+
box-shadow: 0 0 10px 0 rgba(0,0,0,${p => p.theme.boxShadowAlpha});
26+
`;
27+
2128
@inject('sendStore')
2229
@observer
2330
export class SendPage extends React.Component<{
@@ -34,30 +41,45 @@ export class SendPage extends React.Component<{
3441
render() {
3542
const {
3643
sendRequests,
37-
sendRequest
44+
selectRequest,
45+
deleteRequest,
46+
sendRequest,
47+
selectedRequest,
48+
addRequestInput
3849
} = this.props.sendStore!;
3950

40-
const focusedSendRequest = sendRequests[0];
41-
4251
return <SendPageContainer>
43-
<SplitPane
44-
split='vertical'
45-
primary='second'
46-
defaultSize='50%'
47-
minSize={300}
48-
maxSize={-300}
52+
<SendTabs
53+
sendRequests={sendRequests}
54+
selectedTab={selectedRequest}
55+
onSelectTab={selectRequest}
56+
onCloseTab={deleteRequest}
57+
onAddTab={addRequestInput}
58+
/>
59+
60+
<TabContentContainer
61+
id='send-tabpanel'
62+
role='tabpanel'
4963
>
50-
<RequestPane
51-
requestInput={focusedSendRequest.request}
52-
sendRequest={() => sendRequest(focusedSendRequest)}
53-
editorNode={this.requestEditorNode}
54-
/>
55-
<ResponsePane
56-
requestInput={focusedSendRequest.request}
57-
exchange={focusedSendRequest.sentExchange}
58-
editorNode={this.responseEditorNode}
59-
/>
60-
</SplitPane>
64+
<SplitPane
65+
split='vertical'
66+
primary='second'
67+
defaultSize='50%'
68+
minSize={300}
69+
maxSize={-300}
70+
>
71+
<RequestPane
72+
requestInput={selectedRequest.request}
73+
sendRequest={() => sendRequest(selectedRequest)}
74+
editorNode={this.requestEditorNode}
75+
/>
76+
<ResponsePane
77+
requestInput={selectedRequest.request}
78+
exchange={selectedRequest.sentExchange}
79+
editorNode={this.responseEditorNode}
80+
/>
81+
</SplitPane>
82+
</TabContentContainer>
6183

6284
<portals.InPortal node={this.requestEditorNode}>
6385
<ContainerSizedEditor contentId={null} />

src/components/send/send-tabs.tsx

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import * as React from 'react';
2+
3+
import { css, styled } from '../../styles';
4+
import { UnstyledButton } from '../common/inputs';
5+
import { SendRequest } from '../../model/send/send-request-model';
6+
import { getMethodColor } from '../../model/events/categorization';
7+
import { observer } from 'mobx-react';
8+
import { IconButton } from '../common/icon-button';
9+
import { noPropagation } from '../component-utils';
10+
11+
export const TAB_BAR_HEIGHT = '38px';
12+
13+
const TabsContainer = styled.div.attrs(() => ({
14+
role: 'tablist'
15+
}))`
16+
width: 100%;
17+
height: ${TAB_BAR_HEIGHT};
18+
box-sizing: border-box;
19+
20+
background-color: ${p => p.theme.containerBackground};
21+
border-bottom: 1px solid ${p => p.theme.containerWatermark};
22+
23+
font-size: ${p => p.theme.textSize};
24+
25+
display: flex;
26+
flex-direction: row;
27+
align-items: end;
28+
`;
29+
30+
const TabContainer = styled.div<{ selected: boolean }>`
31+
box-sizing: border-box;
32+
33+
width: 200px;
34+
height: ${TAB_BAR_HEIGHT};
35+
36+
flex-grow: 0;
37+
flex-shrink: 1;
38+
overflow: hidden;
39+
40+
display: flex;
41+
flex-direction: row;
42+
43+
${p => p.selected
44+
? css`
45+
margin-bottom: -1px;
46+
47+
border-bottom: 3px solid ${p => p.theme.popColor};
48+
49+
background-color: ${p => p.theme.mainBackground};
50+
box-shadow: 0 0 10px 0 rgba(0,0,0,${p => p.theme.boxShadowAlpha});
51+
z-index: 1;
52+
`
53+
: css`
54+
background-color: ${p => p.theme.mainLowlightBackground};
55+
56+
border-bottom: 3px solid transparent;
57+
58+
&:hover {
59+
background-color: ${p => p.theme.mainBackground};
60+
}
61+
`
62+
}
63+
`;
64+
65+
const TabButton = styled(UnstyledButton).attrs((p: { selected: boolean }) => ({
66+
role: 'tab',
67+
'aria-selected': p.selected.toString(),
68+
'tabindex': p.selected ? '0' : '-1'
69+
}))`
70+
flex-basis: 100%;
71+
flex-grow: 1;
72+
flex-shrink: 1;
73+
74+
text-align: left;
75+
text-overflow: ellipsis;
76+
overflow: hidden;
77+
white-space: nowrap;
78+
79+
padding: 0 10px;
80+
`;
81+
82+
const TabMethodMarker = styled.span<{ method: string }>`
83+
color: ${p => getMethodColor(p.method)};
84+
font-size: ${p => p.theme.textInputFontSize};
85+
margin-right: 5px;
86+
`;
87+
88+
const TabName = styled.span`
89+
&:empty::before {
90+
content: 'New request';
91+
font-style: italic;
92+
opacity: ${p => p.theme.lowlightTextOpacity};
93+
text-align: center;
94+
}
95+
`;
96+
97+
const CloseTabButton = styled(IconButton)`
98+
`;
99+
100+
const AddTabButton = styled(IconButton)`
101+
align-self: center;
102+
`;
103+
104+
export const SendTabs = observer((props: {
105+
sendRequests: Array<SendRequest>;
106+
selectedTab: SendRequest;
107+
onSelectTab: (sendRequest: SendRequest) => void;
108+
onCloseTab: (sendRequest: SendRequest) => void;
109+
onAddTab: () => void;
110+
}) => {
111+
112+
113+
return <TabsContainer>
114+
{
115+
props.sendRequests.map((sendRequest) => {
116+
const { id, request } = sendRequest;
117+
118+
const isSelected = props.selectedTab === sendRequest;
119+
120+
return <TabContainer
121+
key={id}
122+
selected={isSelected}
123+
onClick={() => props.onSelectTab(sendRequest)}
124+
>
125+
<TabButton selected={isSelected}>
126+
<TabMethodMarker method={request.method}>
127+
{ request.method }
128+
</TabMethodMarker>
129+
130+
<TabName>{
131+
request.url.replace(/^https?:\/\//, '') || ''
132+
}</TabName>
133+
</TabButton>
134+
135+
{
136+
isSelected && <CloseTabButton
137+
title='Close this tab'
138+
icon={['fas', 'times']}
139+
onClick={noPropagation(() => props.onCloseTab(sendRequest))}
140+
/>
141+
}
142+
</TabContainer>;
143+
})
144+
}
145+
146+
<AddTabButton
147+
title='Add another tab to send a new request'
148+
icon={['fas', 'plus']}
149+
onClick={() => props.onAddTab()}
150+
/>
151+
</TabsContainer>
152+
});

src/model/send/send-request-model.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ export class RequestInput {
6767
}
6868

6969
export interface SendRequest {
70-
request: RequestInput,
71-
sentExchange: HttpExchange | undefined
70+
id: string;
71+
request: RequestInput;
72+
sentExchange: HttpExchange | undefined;
7273
}
7374

7475
const requestInputSchema = serializr.createModelSchema(RequestInput, {
@@ -91,6 +92,7 @@ const requestInputSchema = serializr.createModelSchema(RequestInput, {
9192
});
9293

9394
export const sendRequestSchema = serializr.createSimpleSchema({
95+
id: serializr.primitive(),
9496
request: serializr.object(requestInputSchema),
9597
sentExchange: false // Never persisted here
9698
});

0 commit comments

Comments
 (0)