1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React, { useEffect, useRef, useState } from 'react';
12 } from '@material-ui/core/styles';
13 import grey from '@material-ui/core/colors/grey';
14 import { ArvadosTheme } from 'common/custom-theme';
15 import { Link, Typography } from '@material-ui/core';
16 import { navigateTo } from 'store/navigation/navigation-action';
17 import { Dispatch } from 'redux';
18 import { connect, DispatchProp } from 'react-redux';
19 import classNames from 'classnames';
21 type CssRules = 'root' | 'wordWrap' | 'logText';
23 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
25 boxSizing: 'border-box',
27 backgroundColor: '#000',
28 height: `calc(100% - ${theme.spacing.unit * 4}px)`, // so that horizontal scollbar is visible
31 padding: theme.spacing.unit * 0.5,
34 whiteSpace: 'pre-wrap',
38 const theme = createMuiTheme({
47 fontFamily: 'monospace',
48 useNextVariants: true,
52 interface ProcessLogCodeSnippetProps {
58 const renderLinks = (fontSize: number, dispatch: Dispatch) => (text: string) => {
59 // Matches UUIDs & PDHs
60 const REGEX = /[a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}|[0-9a-f]{32}\+\d+/g;
61 const links = text.match(REGEX);
63 return <Typography style={{ fontSize: fontSize }}>{text}</Typography>;
65 return <Typography style={{ fontSize: fontSize }}>
66 {text.split(REGEX).map((part, index) =>
67 <React.Fragment key={index}>
70 <Link onClick={() => dispatch<any>(navigateTo(links[index]))}
71 style={ {cursor: 'pointer'} }>
79 export const ProcessLogCodeSnippet = withStyles(styles)(connect()(
80 ({classes, lines, fontSize, dispatch, wordWrap}: ProcessLogCodeSnippetProps & WithStyles<CssRules> & DispatchProp) => {
81 const [followMode, setFollowMode] = useState<boolean>(false);
82 const scrollRef = useRef<HTMLDivElement>(null);
85 if (followMode && scrollRef.current && lines.length > 0) {
87 scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
89 }, [followMode, lines, scrollRef]);
91 return <MuiThemeProvider theme={theme}>
92 <div ref={scrollRef} className={classes.root}
94 const elem = e.target as HTMLDivElement;
95 if (elem.scrollTop + elem.clientHeight >= elem.scrollHeight) {
101 { lines.map((line: string, index: number) =>
102 <Typography key={index} component="pre"
103 className={classNames(classes.logText, wordWrap ? classes.wordWrap : undefined)}>
104 {renderLinks(fontSize, dispatch)(line)}