Merge branch '21832-installer-rds-support'
[arvados.git] / services / workbench2 / src / components / code-snippet / virtual-code-snippet.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from 'react';
6 import { StyleRulesCallback, WithStyles, Typography, withStyles, Tooltip, IconButton } from '@material-ui/core';
7 import { ArvadosTheme } from 'common/custom-theme';
8 import classNames from 'classnames';
9 import { connect } from 'react-redux';
10 import { RootState } from 'store/store';
11 import { FederationConfig } from 'routes/routes';
12 import { renderLinks } from './code-snippet';
13 import { FixedSizeList } from 'react-window';
14 import AutoSizer from "react-virtualized-auto-sizer";
15 import CopyResultToClipboard from 'components/copy-to-clipboard/copy-result-to-clipboard';
16 import { CopyIcon } from 'components/icon/icon';
17 import { SnackbarKind, snackbarActions } from 'store/snackbar/snackbar-actions';
18 import { Dispatch } from "redux";
19
20 type CssRules = 'root' | 'space' | 'content' | 'copyButton' ;
21
22 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
23     root: {
24         position: 'relative',
25         boxSizing: 'border-box',
26         height: '100%',
27         padding: theme.spacing.unit,
28     },
29     space: {
30         marginLeft: '15px',
31     },
32     content: {
33         maxHeight: '100%',
34         height: '100vh',
35     },
36     copyButton: {
37         position: 'absolute',
38         top: '8px',
39         right: '16px',
40         zIndex: 100,
41     },
42 });
43
44 export interface CodeSnippetDataProps {
45     lines: string[];
46     lineFormatter?: (lines: string[], index: number) => string;
47     className?: string;
48     apiResponse?: boolean;
49     linked?: boolean;
50     copyButton?: boolean;
51 }
52
53 export interface CodeSnippetActionProps {
54     renderLinks: (auth: FederationConfig) => (text: string) => JSX.Element;
55     onCopyToClipboard: () => void;
56 }
57
58 interface CodeSnippetAuthProps {
59     auth: FederationConfig;
60 }
61
62 type CodeSnippetProps = CodeSnippetDataProps & CodeSnippetActionProps & WithStyles<CssRules>;
63
64 const mapStateToProps = (state: RootState): CodeSnippetAuthProps => ({
65     auth: state.auth,
66 });
67
68 const mapDispatchToProps = (dispatch: Dispatch): CodeSnippetActionProps => ({
69     renderLinks: (auth: FederationConfig) => renderLinks(auth, dispatch),
70     onCopyToClipboard: () => {
71         dispatch<any>(
72             snackbarActions.OPEN_SNACKBAR({
73                 message: "Contents copied to clipboard",
74                 hideDuration: 2000,
75                 kind: SnackbarKind.SUCCESS,
76             })
77         );
78     },
79 });
80
81 export const VirtualCodeSnippet = withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(
82     ({ classes, lines, lineFormatter, linked, copyButton, renderLinks, onCopyToClipboard, className, apiResponse, auth }: CodeSnippetProps & CodeSnippetAuthProps) => {
83         const RenderRow = ({index, style}) => {
84             const lineContents = lineFormatter ? lineFormatter(lines, index) : lines[index];
85             return <span style={style}>{linked ? renderLinks(auth)(lineContents) : lineContents}</span>
86         };
87
88         const formatClipboardText = (lines: string[]) => () =>  {
89             return lines.join('\n');
90         };
91
92
93
94         return <Typography
95             component="div"
96             className={classNames([classes.root, className])}>
97             {copyButton && <span className={classes.copyButton}>
98                 <Tooltip title="Copy text to clipboard" disableFocusListener>
99                     <IconButton>
100                         <CopyResultToClipboard
101                             getText={formatClipboardText(lines)}
102                             onCopy={onCopyToClipboard}
103                         >
104                             <CopyIcon />
105                         </CopyResultToClipboard>
106                     </IconButton>
107                 </Tooltip>
108             </span>}
109             <Typography className={classNames(classes.content, apiResponse ? classes.space : className)} component="pre">
110                 <AutoSizer>
111                     {({ height, width }) =>
112                         <FixedSizeList
113                             height={height}
114                             width={width}
115                             itemSize={21}
116                             itemCount={lines.length}
117                         >
118                             {RenderRow}
119                         </FixedSizeList>
120                     }
121                 </AutoSizer>
122             </Typography>
123         </Typography>;
124 }));