19231: Add smaller page sizes (10 and 20 items) to load faster
[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 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 CopyToClipboard from 'react-copy-to-clipboard';
12 import { ArvadosTheme } from 'common/custom-theme';
13 import 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         marginBottom: ".6 rem"
24     },
25     label: {
26         boxSizing: 'border-box',
27         color: theme.palette.grey["500"],
28         width: '100%'
29     },
30     value: {
31         boxSizing: 'border-box',
32         alignItems: 'flex-start'
33     },
34     lowercaseValue: {
35         textTransform: 'lowercase'
36     },
37     link: {
38         color: theme.palette.primary.main,
39         textDecoration: 'none',
40         overflowWrap: 'break-word',
41         cursor: 'pointer'
42     },
43     copyIcon: {
44         marginLeft: theme.spacing.unit,
45         color: theme.palette.grey["500"],
46         cursor: 'pointer',
47         display: 'inline',
48         '& svg': {
49             fontSize: '1rem'
50         }
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     copyValue?: string;
65     uuidEnhancer?: Function;
66 }
67
68 type DetailsAttributeProps = DetailsAttributeDataProps & WithStyles<CssRules> & FederationConfig & DispatchProp;
69
70 const mapStateToProps = ({ auth }: RootState): FederationConfig => ({
71     localCluster: auth.localCluster,
72     remoteHostsConfig: auth.remoteHostsConfig,
73     sessions: auth.sessions
74 });
75
76 export const DetailsAttribute = connect(mapStateToProps)(withStyles(styles)(
77     class extends React.Component<DetailsAttributeProps> {
78
79         onCopy = (message: string) => {
80             this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
81                 message,
82                 hideDuration: 2000,
83                 kind: SnackbarKind.SUCCESS
84             }));
85         }
86
87         render() {
88             const { uuidEnhancer, link, value, classes, linkToUuid,
89                 localCluster, remoteHostsConfig, sessions } = this.props;
90             let valueNode: React.ReactNode;
91
92             if (linkToUuid) {
93                 const uuid = uuidEnhancer ? uuidEnhancer(linkToUuid) : linkToUuid;
94                 const linkUrl = getNavUrl(linkToUuid || "", { localCluster, remoteHostsConfig, sessions });
95                 if (linkUrl[0] === '/') {
96                     valueNode = <Link to={linkUrl} className={classes.link}>{uuid}</Link>;
97                 } else {
98                     valueNode = <a href={linkUrl} className={classes.link} target='_blank' rel="noopener noreferrer">{uuid}</a>;
99                 }
100             } else if (link) {
101                 valueNode = <a href={link} className={classes.link} target='_blank' rel="noopener noreferrer">{value}</a>;
102             } else {
103                 valueNode = value;
104             }
105
106             return <DetailsAttributeComponent {...this.props} value={valueNode} onCopy={this.onCopy} />;
107         }
108     }
109 ));
110
111 interface DetailsAttributeComponentProps {
112     value: React.ReactNode;
113     onCopy?: (msg: string) => void;
114 }
115
116 export const DetailsAttributeComponent = withStyles(styles)(
117     (props: DetailsAttributeDataProps & WithStyles<CssRules> & DetailsAttributeComponentProps) =>
118         <Typography component="div" className={props.classes.attribute}>
119             <Typography component="div" className={classnames([props.classes.label, props.classLabel])}>{props.label}</Typography>
120             <Typography
121                 onClick={props.onValueClick}
122                 component="div"
123                 className={classnames([props.classes.value, props.classValue, { [props.classes.lowercaseValue]: props.lowercaseValue }])}>
124                 {props.value}
125                 {props.children}
126                 {(props.linkToUuid || props.copyValue) && props.onCopy && <Tooltip title="Copy to clipboard">
127                     <span className={props.classes.copyIcon}>
128                         <CopyToClipboard text={props.linkToUuid || props.copyValue || ""} onCopy={() => props.onCopy!("Copied")}>
129                             <CopyIcon />
130                         </CopyToClipboard>
131                     </span>
132                 </Tooltip>}
133             </Typography>
134         </Typography>);
135