16719: Merge branch 'master' into 16719-collection-version-basic-ui
[arvados-workbench2.git] / src / components / details-attribute / details-attribute.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { connect, DispatchProp } from 'react-redux';
7 import Typography from '@material-ui/core/Typography';
8 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
9 import { Tooltip } from '@material-ui/core';
10 import { CopyIcon } from '~/components/icon/icon';
11 import * as CopyToClipboard from 'react-copy-to-clipboard';
12 import { ArvadosTheme } from '~/common/custom-theme';
13 import * as classnames from "classnames";
14 import { Link } from 'react-router-dom';
15 import { RootState } from "~/store/store";
16 import { FederationConfig, getNavUrl } from "~/routes/routes";
17 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
18
19 type CssRules = 'attribute' | 'label' | 'value' | 'lowercaseValue' | 'link' | 'copyIcon';
20
21 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
22     attribute: {
23         display: 'flex',
24         alignItems: 'flex-start'
25     },
26     label: {
27         boxSizing: 'border-box',
28         color: theme.palette.grey["500"],
29         width: '40%'
30     },
31     value: {
32         boxSizing: 'border-box',
33         width: '60%',
34         alignItems: 'flex-start'
35     },
36     lowercaseValue: {
37         textTransform: 'lowercase'
38     },
39     link: {
40         width: '60%',
41         color: theme.palette.primary.main,
42         textDecoration: 'none',
43         overflowWrap: 'break-word',
44         cursor: 'pointer'
45     },
46     copyIcon: {
47         marginLeft: theme.spacing.unit,
48         fontSize: '1.125rem',
49         color: theme.palette.grey["500"],
50         cursor: 'pointer'
51     }
52 });
53
54 interface DetailsAttributeDataProps {
55     label: string;
56     classLabel?: string;
57     value?: React.ReactNode;
58     classValue?: string;
59     lowercaseValue?: boolean;
60     link?: string;
61     children?: React.ReactNode;
62     onValueClick?: () => void;
63     linkToUuid?: string;
64 }
65
66 type DetailsAttributeProps = DetailsAttributeDataProps & WithStyles<CssRules> & FederationConfig & DispatchProp;
67
68 const mapStateToProps = ({ auth }: RootState): FederationConfig => ({
69     localCluster: auth.localCluster,
70     remoteHostsConfig: auth.remoteHostsConfig,
71     sessions: auth.sessions
72 });
73
74 export const DetailsAttribute = connect(mapStateToProps)(withStyles(styles)(
75     class extends React.Component<DetailsAttributeProps> {
76
77         onCopy = (message: string) => {
78             this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
79                 message,
80                 hideDuration: 2000,
81                 kind: SnackbarKind.SUCCESS
82             }));
83         }
84
85         render() {
86             const { label, link, value, children, classes, classLabel,
87                 classValue, lowercaseValue, onValueClick, linkToUuid,
88                 localCluster, remoteHostsConfig, sessions } = this.props;
89             let valueNode: React.ReactNode;
90
91             if (linkToUuid) {
92                 const linkUrl = getNavUrl(linkToUuid || "", { localCluster, remoteHostsConfig, sessions });
93                 if (linkUrl[0] === '/') {
94                     valueNode = <Link to={linkUrl} className={classes.link}>{linkToUuid}</Link>;
95                 } else {
96                     valueNode = <a href={linkUrl} className={classes.link} target='_blank'>{linkToUuid}</a>;
97                 }
98             } else if (link) {
99                 valueNode = <a href={link} className={classes.link} target='_blank'>{value}</a>;
100             } else {
101                 valueNode = value;
102             }
103             return <Typography component="div" className={classes.attribute}>
104                 <Typography component="span" className={classnames([classes.label, classLabel])}>{label}</Typography>
105                 <Typography
106                     onClick={onValueClick}
107                     component="span"
108                     className={classnames([classes.value, classValue, { [classes.lowercaseValue]: lowercaseValue }])}>
109                     {valueNode}
110                     {children}
111                     {linkToUuid && <Tooltip title="Copy">
112                         <CopyToClipboard text={linkToUuid || ""} onCopy={() => this.onCopy("Copied")}>
113                             <CopyIcon className={classes.copyIcon} />
114                         </CopyToClipboard>
115                     </Tooltip>}
116                 </Typography>
117             </Typography>;
118         }
119     }
120 ));