Skip to content

Commit 359bbcb

Browse files
committed
feature: Basic context menu example
1 parent 9acf2b7 commit 359bbcb

File tree

5 files changed

+106
-2
lines changed

5 files changed

+106
-2
lines changed

automation/webpack.common.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ export default <Webpack.Configuration>{
7171
}, {
7272
test: /\.(woff2|ttf|png|svg)$/,
7373
loader: 'file-loader'
74+
}, {
75+
test: /\.mjs$/,
76+
include: /node_modules/,
77+
type: "javascript/auto"
7478
}, {
7579
test: /\.css$/,
7680
use: ['style-loader', 'css-loader']

package-lock.json

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"react": "^16.14.0",
122122
"react-autosuggest": "^10.0.4",
123123
"react-beautiful-dnd": "^12.2.0",
124+
"react-contexify": "^6.0.0",
124125
"react-dom": "^16.14.0",
125126
"react-hotkeys-hook": "^2.1.3",
126127
"react-monaco-editor": "^0.45.0",

src/components/view/view-event-list.tsx

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as _ from 'lodash';
22
import * as React from 'react';
33
import { observer, Observer } from 'mobx-react';
4-
import { action, computed } from 'mobx';
4+
import { action, computed, runInAction } from 'mobx';
55

66
import AutoSizer from 'react-virtualized-auto-sizer';
77
import { FixedSizeList as List, ListChildComponentProps } from 'react-window';
@@ -33,6 +33,22 @@ import { StatusCode } from '../common/status-code';
3333

3434
import { HEADER_FOOTER_HEIGHT } from './view-event-list-footer';
3535

36+
import {
37+
Menu,
38+
Item,
39+
Separator,
40+
Submenu,
41+
useContextMenu,
42+
ItemProps,
43+
ItemParams,
44+
} from "react-contexify";
45+
const MENU_VIEW_EVENT_ID = "MENU_VIEW_EVENT_LIST";
46+
47+
48+
const { show } = useContextMenu({
49+
id: MENU_VIEW_EVENT_ID
50+
});
51+
type ItemData = any;
3652
const SCROLL_BOTTOM_MARGIN = 5; // If you're in the last 5 pixels of the scroll area, we say you're at the bottom
3753

3854
const EmptyStateOverlay = styled(EmptyState)`
@@ -380,7 +396,7 @@ const ExchangeRow = observer(({
380396
aria-rowindex={index + 1}
381397
data-event-id={exchange.id}
382398
tabIndex={isSelected ? 0 : -1}
383-
399+
onContextMenu={e => displayMenu(e,exchange)}
384400
className={isSelected ? 'selected' : ''}
385401
style={style}
386402
>
@@ -647,7 +663,47 @@ const TlsRow = observer((p: {
647663
} connection to { tlsEvent.upstreamHostname || 'unknown domain' }
648664
</TlsListRow>
649665
});
666+
const UTF8Decoder = new TextDecoder('utf8', { fatal: true });
667+
668+
async function copyToClipboard(textToCopy: string) {
669+
try {
670+
if (navigator.clipboard && window.isSecureContext)
671+
await navigator.clipboard.writeText(textToCopy);
672+
else {
673+
const textArea = document.createElement("textarea");
674+
textArea.value = textToCopy;
675+
textArea.style.position = "absolute";
676+
textArea.style.left = "-9999px";
677+
document.body.prepend(textArea);
678+
textArea.select();
679+
try {
680+
document.execCommand('copy');
681+
} catch (error) {
682+
console.error(error);
683+
} finally {
684+
textArea.remove();
685+
}
686+
};
687+
} catch (error) {
688+
console.error("Clipboard copy failure", error);
689+
}
690+
}
691+
const ContextMenuItemClicked = ( { id, event, props, data, triggerEvent }: ItemParams<ItemProps, ItemData> ) => {
692+
let exchange = (props as any).exchange as HttpExchange;
693+
switch(id) {
694+
case "TogglePin":
695+
runInAction(() => exchange.pinned = ! exchange.pinned );
696+
break;
697+
case "DecodedBody":
698+
if (exchange && exchange.hasResponseBody() && exchange.response.body )
699+
exchange.response.body.decodedPromise.then( val => { copyToClipboard(UTF8Decoder.decode(val)) });
700+
break;
701+
}
650702

703+
};
704+
function displayMenu(e: React.MouseEvent, exchange : HttpExchange) {
705+
show({event: e, props: { exchange: exchange } });
706+
}
651707
@observer
652708
export class ViewEventList extends React.Component<ViewEventListProps> {
653709

@@ -734,6 +790,14 @@ export class ViewEventList extends React.Component<ViewEventListProps> {
734790
}</Observer>
735791
}</AutoSizer>
736792
}
793+
794+
<Menu id={MENU_VIEW_EVENT_ID}>
795+
<Item id="TogglePin" onClick={ContextMenuItemClicked}>Toggle Pinned</Item>
796+
<Separator />
797+
<Submenu label="Copy">
798+
<Item id="DecodedBody" onClick={ContextMenuItemClicked}>Decoded Body</Item>
799+
</Submenu>
800+
</Menu>
737801
</ListContainer>;
738802
}
739803

src/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const fontSizes = {
1313
largeHeadingSize: '24px',
1414
loudHeadingSize: '38px',
1515
};
16+
import "react-contexify/dist/ReactContexify.css";
1617

1718
export const NARROW_LAYOUT_BREAKPOINT = 1100;
1819

0 commit comments

Comments
 (0)