[8.5][Session view][TTY output] UI Improvements (#141175)

This commit is contained in:
Paulo Henrique 2022-09-20 17:22:32 -07:00 committed by GitHub
parent 99e367446b
commit 4c0c0db9c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 67 deletions

View file

@ -75,7 +75,7 @@ export const useStyles = (tty?: Teletype, show?: boolean) => {
transform: `translateY(${show ? 0 : '100%'})`,
transition: 'transform .2s',
width: '100%',
height: 'calc(100% - 120px)',
height: 'calc(100% - 112px)',
overflow: 'auto',
backgroundColor: colors.ink,
};

View file

@ -12,6 +12,7 @@ import {
EuiFlexItem,
EuiButtonIcon,
EuiToolTip,
EuiButtonIconProps,
} from '@elastic/eui';
import { findIndex } from 'lodash';
import { ProcessStartMarker, ProcessEvent } from '../../../common/types/process_tree';
@ -54,6 +55,13 @@ export const TTYPlayerControls = ({
}: TTYPlayerControlsDeps) => {
const styles = useStyles();
const commonButtonProps: Partial<EuiButtonIconProps> = {
display: 'empty',
size: 's',
color: 'ghost',
css: styles.controlButton,
};
const onLineChange = useCallback(
(event: ChangeEvent<HTMLInputElement> | MouseEvent<HTMLButtonElement>) => {
const line = parseInt((event?.target as HTMLInputElement).value || '0', 10);
@ -111,65 +119,55 @@ export const TTYPlayerControls = ({
<EuiFlexItem grow={false}>
<EuiToolTip content={TTY_START}>
<EuiButtonIcon
css={styles.controlButton}
data-test-subj="sessionView:TTYPlayerControlsStart"
iconType="arrowStart"
display="empty"
size="m"
aria-label={TTY_START}
onClick={seekToStart}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip content={TTY_PREVIOUS}>
<EuiButtonIcon
css={styles.controlButton}
data-test-subj="sessionView:TTYPlayerControlsPrevious"
iconType="arrowLeft"
display="empty"
size="m"
aria-label={TTY_PREVIOUS}
onClick={seekToPrevProcess}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip content={isPlaying ? TTY_PAUSE : TTY_PLAY}>
<EuiButtonIcon
css={styles.controlButton}
data-test-subj="sessionView:TTYPlayerControlsPlay"
iconType={isPlaying ? 'pause' : 'playFilled'}
display="empty"
size="m"
aria-label={isPlaying ? TTY_PAUSE : TTY_PLAY}
onClick={onTogglePlayback}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip content={TTY_NEXT}>
<EuiButtonIcon
css={styles.controlButton}
data-test-subj="sessionView:TTYPlayerControlsNext"
iconType="arrowRight"
display="empty"
size="m"
aria-label={TTY_NEXT}
onClick={seekToNextProcess}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip content={TTY_END}>
<EuiButtonIcon
css={styles.controlButton}
data-test-subj="sessionView:TTYPlayerControlsEnd"
iconType="arrowEnd"
display="empty"
size="m"
aria-label={TTY_END}
onClick={seekToEnd}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>
@ -179,6 +177,7 @@ export const TTYPlayerControls = ({
linesLength={linesLength}
currentLine={currentLine}
onChange={onLineChange}
onSeekLine={onSeekLine}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
@ -188,6 +187,7 @@ export const TTYPlayerControls = ({
onClick={handleViewInSession}
iconType="arrowRight"
aria-label={VIEW_IN_SESSION}
color="ghost"
>
{VIEW_IN_SESSION}
</EuiButtonEmpty>

View file

@ -6,7 +6,7 @@
*/
import React, { ChangeEvent, MouseEvent, useMemo } from 'react';
import { EuiRange } from '@elastic/eui';
import { EuiRange, EuiToolTip } from '@elastic/eui';
import type { ProcessStartMarker } from '../../../../common/types/process_tree';
import { useStyles } from './styles';
import { PlayHead } from './play_head';
@ -16,11 +16,13 @@ type Props = {
linesLength: number;
currentLine: number;
onChange: (e: ChangeEvent<HTMLInputElement> | MouseEvent<HTMLButtonElement>) => void;
onSeekLine(line: number): void;
};
type TTYPlayerLineMarker = {
line: number;
type: 'output' | 'data_limited';
name: string;
};
export const TTYPlayerControlsMarkers = ({
@ -28,8 +30,11 @@ export const TTYPlayerControlsMarkers = ({
linesLength,
currentLine,
onChange,
onSeekLine,
}: Props) => {
const styles = useStyles();
const progress = useMemo(() => (currentLine / linesLength) * 100, [currentLine, linesLength]);
const styles = useStyles(progress);
const markers = useMemo(() => {
if (processStartMarkers.length < 1) {
@ -41,22 +46,29 @@ export const TTYPlayerControlsMarkers = ({
type:
event.process?.io?.max_bytes_per_process_exceeded === true ? 'data_limited' : 'output',
line,
name: event.process?.name,
} as TTYPlayerLineMarker)
);
}, [processStartMarkers]);
const markersLength = markers.length;
const currentSelectedType = useMemo(() => {
if (!markersLength) {
return undefined;
}
const currentSelected =
currentLine >= markers[markersLength - 1].line
? markersLength - 1
: markers.findIndex((marker) => marker.line > currentLine) - 1;
return markers[Math.max(0, currentSelected)].type;
}, [currentLine, markers, markersLength]);
if (!markersLength) {
return null;
}
const currentSelected =
currentLine >= markers[markersLength - 1].line
? markersLength - 1
: markers.findIndex((marker) => marker.line > currentLine) - 1;
const currentSelectedType = markers[Math.max(0, currentSelected)].type;
return (
<>
<EuiRange
@ -68,35 +80,38 @@ export const TTYPlayerControlsMarkers = ({
showRange
css={styles.range}
/>
<PlayHead
css={styles.playHead(currentSelectedType)}
style={{ left: `${(currentLine * 100) / linesLength}%` }}
/>
<PlayHead css={styles.playHead(currentSelectedType)} />
<div css={styles.markersOverlay}>
{markers.map(({ line, type }, idx) => {
{markers.map(({ line, type, name }, idx) => {
const selected =
currentLine >= line &&
(idx === markersLength - 1 || currentLine < markers[idx + 1].line);
// markers positions are absolute, setting higher z-index on the selected one in case there
// are severals next to each other
const style = {
left: `${(line * 100) / linesLength}%`,
const markerWrapperPositioning = {
left: `${(line / linesLength) * 100}%`,
zIndex: selected ? 3 : 2,
};
const onMarkerClick = () => onSeekLine(line);
return (
<button
key={idx}
type="button"
value={line}
tabIndex={-1}
title={type}
css={styles.marker(type, selected)}
style={style}
>
{type}
</button>
<div key={idx} style={markerWrapperPositioning} css={styles.markerWrapper}>
<EuiToolTip title={name}>
<button
type="button"
value={line}
tabIndex={-1}
title={type}
css={styles.marker(type, selected)}
onClick={onMarkerClick}
aria-label={name}
>
{name}
</button>
</EuiToolTip>
</div>
);
})}
</div>

View file

@ -11,7 +11,7 @@ import { useEuiTheme } from '../../../hooks';
type TTYPlayerLineMarkerType = 'output' | 'data_limited';
export const useStyles = () => {
export const useStyles = (progress: number) => {
const { euiTheme, euiVars } = useEuiTheme();
const cached = useMemo(() => {
const { border } = euiTheme;
@ -23,6 +23,12 @@ export const useStyles = () => {
width: '100%',
};
const markerWrapper: CSSObject = {
position: 'absolute',
top: 0,
lineHeight: 0,
};
const getMarkerBackgroundColor = (type: TTYPlayerLineMarkerType, selected: boolean) => {
if (type === 'data_limited') {
return euiVars.terminalOutputMarkerWarning;
@ -36,7 +42,6 @@ export const useStyles = () => {
const marker = (type: TTYPlayerLineMarkerType, selected: boolean): CSSObject => ({
fontSize: 0,
overflow: 'hidden',
position: 'absolute',
padding: 0,
width: 3,
height: 12,
@ -44,10 +49,8 @@ export const useStyles = () => {
border: `${border.width.thick} solid ${euiVars.terminalOutputBackground}`,
borderRadius: border.radius.small,
boxSizing: 'content-box',
top: 0,
pointerEvents: 'none',
marginLeft: '-3.5px',
transition: 'left .5s ease-in-out .3s',
transition: 'left .5s ease-in-out',
});
const playHeadThumb: CSSObject = {
@ -82,6 +85,9 @@ export const useStyles = () => {
"input[type='range']::-moz-range-thumb": customThumb,
'.euiRangeHighlight__progress': {
backgroundColor: euiVars.euiColorVis0_behindText,
width: progress + '%',
borderBottomRightRadius: 0,
borderTopRightRadius: 0,
},
'.euiRangeSlider:focus ~ .euiRangeHighlight .euiRangeHighlight__progress': {
backgroundColor: euiVars.euiColorVis0_behindText,
@ -93,9 +99,10 @@ export const useStyles = () => {
},
};
const playHead = (type: TTYPlayerLineMarkerType): CSSObject => ({
const playHead = (type?: TTYPlayerLineMarkerType): CSSObject => ({
...playHeadThumb,
position: 'absolute',
left: progress + '%',
top: 16,
fill:
type === 'data_limited'
@ -105,11 +112,21 @@ export const useStyles = () => {
return {
marker,
markerWrapper,
markersOverlay,
range,
playHead,
};
}, [euiTheme, euiVars]);
}, [
euiTheme,
euiVars.euiColorVis0_behindText,
euiVars.euiColorVis1,
euiVars.terminalOutputBackground,
euiVars.terminalOutputMarkerAccent,
euiVars.terminalOutputMarkerWarning,
euiVars.terminalOutputSliderBackground,
progress,
]);
return cached;
};

View file

@ -5,7 +5,13 @@
* 2.0.
*/
import React, { useCallback, useEffect, useState } from 'react';
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import {
EuiButtonIcon,
EuiButtonIconProps,
EuiFlexGroup,
EuiFlexItem,
EuiToolTip,
} from '@elastic/eui';
import { Teletype } from '../../../common/types/process_tree';
import { DEFAULT_TTY_FONT_SIZE } from '../../../common/constants';
import { ZOOM_IN, ZOOM_FIT, ZOOM_OUT } from './translations';
@ -19,6 +25,12 @@ export interface TTYTextSizerDeps {
onFontSizeChanged(newSize: number): void;
}
const commonButtonProps: Partial<EuiButtonIconProps> = {
display: 'empty',
size: 's',
color: 'ghost',
};
const LINE_HEIGHT_SCALE_RATIO = 1.3;
const MINIMUM_FONT_SIZE = 2;
const MAXIMUM_FONT_SIZE = 20;
@ -88,25 +100,13 @@ export const TTYTextSizer = ({
display={fit ? 'fill' : 'empty'}
iconType={fit ? 'expand' : 'minimize'}
onClick={onToggleFit}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem>
<div css={styles.separator} />
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip content={ZOOM_IN}>
<EuiButtonIcon
data-test-subj="sessionView:TTYZoomIn"
aria-label={ZOOM_IN}
iconType="plusInCircle"
onClick={onZoomIn}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem component="span" css={styles.ratio}>
{`${Math.round((fontSize / DEFAULT_TTY_FONT_SIZE) * 100)}%`}
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip content={ZOOM_OUT}>
<EuiButtonIcon
@ -114,6 +114,21 @@ export const TTYTextSizer = ({
aria-label={ZOOM_OUT}
iconType="minusInCircle"
onClick={onZoomOut}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem component="span" css={styles.ratio}>
{`${Math.round((fontSize / DEFAULT_TTY_FONT_SIZE) * 100)}%`}
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip content={ZOOM_IN}>
<EuiButtonIcon
data-test-subj="sessionView:TTYZoomIn"
aria-label={ZOOM_IN}
iconType="plusInCircle"
onClick={onZoomIn}
{...commonButtonProps}
/>
</EuiToolTip>
</EuiFlexItem>

View file

@ -16,14 +16,13 @@ export const useStyles = () => {
const ratio: CSSObject = {
fontSize: size.m,
color: colors.ghost,
};
const separator: CSSObject = {
background: colors.lightShade,
height: size.xl,
width: border.width.thin,
marginLeft: size.xs,
marginRight: size.xs,
};
return {

View file

@ -11,7 +11,7 @@ export const ZOOM_IN = i18n.translate('xpack.sessionView.zoomIn', {
});
export const ZOOM_FIT = i18n.translate('xpack.sessionView.zoomFit', {
defaultMessage: 'Zoom fit',
defaultMessage: 'Fit screen',
});
export const ZOOM_OUT = i18n.translate('xpack.sessionView.zoomOut', {