Skip to content

Commit

Permalink
feat: add tailing UI to log viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
Caleb Kang committed Jul 28, 2020
1 parent 2452b12 commit 3e59bd1
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 28 deletions.
44 changes: 39 additions & 5 deletions webui/react/src/components/LogViewer.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,52 @@
.scrollTo > *:not(:first-child) {
margin-top: var(--theme-sizes-layout-big);
}
.scrollToTop,
.scrollToLatest {
.scrollTo > button {
border-color: var(--theme-colors-monochrome-9);
box-shadow: var(--theme-shadow);
opacity: 0.2;
color: var(--theme-colors-monochrome-9);
transition: opacity 0.2s linear;
user-select: none;
}
.scrollToTop.show,
.scrollToLatest.show {
.scrollToTop {
opacity: 0.2;
}
.scrollToTop:focus {
border-color: var(--theme-colors-core-action);
color: var(--theme-colors-monochrome-9);

&::after {
border-radius: var(--theme-sizes-border-radius);
box-shadow: var(--theme-outline);
content: '';
height: 100%;
position: absolute;
width: 100%;
}
}
.scrollToTop.show {
opacity: 1;
user-select: auto;
}
.enableTailing {
transition: all 0.2s linear;
}
.enableTailing:focus {
border-color: var(--theme-colors-core-action);

&::after {
border-radius: var(--theme-sizes-border-radius);
box-shadow: var(--theme-outline);
content: '';
height: 100%;
position: absolute;
width: 100%;
}
}
.enableTailing.enabled {
border-color: var(--theme-colors-core-action);
color: var(--theme-colors-core-action);
}
.debugger {
display: flex;
padding: 0 var(--theme-sizes-layout-medium);
Expand Down
49 changes: 26 additions & 23 deletions webui/react/src/components/LogViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,27 +113,25 @@ const LogViewer: React.FC<Props> = forwardRef((
const [ logIdRange, setLogIdRange ] =
useState({ max: Number.MIN_SAFE_INTEGER, min: Number.MAX_SAFE_INTEGER });
const [ scrollToInfo, setScrollToInfo ] =
useState({ isBottom: false, isPrepend: false, logId: 0 });
useState({ isPrepend: false, logId: 0 });
const [ config, setConfig ] = useState<LogConfig>(defaultLogConfig);
const [ isDownloading, setIsDownloading ] = useState<boolean>(false);
const [ isTailing, setIsTailing ] = useState(true);
const previousScroll = usePrevious(scroll, defaultScrollInfo);
const previousLogs = usePrevious<Log[]>(logs, []);
const classes = [ css.base ];
const scrollToTopClasses = [ css.scrollToTop ];
const scrollToLatestClasses = [ css.scrollToLatest ];
const enableTailingClasses = [ css.enableTailing ];

const spacerStyle = { height: toRem(config.totalContentHeight) };
const dateTimeStyle = { width: toRem(config.dateTimeWidth) };
const lineNumberStyle = { width: toRem(config.lineNumberWidth) };
const levelStyle = { width: toRem(ICON_WIDTH) };

if (props.noWrap) classes.push(css.noWrap);
if (scroll.scrollTop > SCROLL_TOP_THRESHOLD) {
scrollToTopClasses.push(css.show);
}
if (scroll.scrollTop < scroll.scrollHeight - scroll.viewHeight - SCROLL_BOTTOM_THRESHOLD) {
scrollToLatestClasses.push(css.show);
}
if (scroll.scrollTop > SCROLL_TOP_THRESHOLD) scrollToTopClasses.push(css.show);
// if (scroll.scrollTop > scroll.scrollHeight - scroll.viewHeight - SCROLL_BOTTOM_THRESHOLD) {
if (isTailing) enableTailingClasses.push(css.enabled);

/*
* Calculate all the sizes of the log pieces such as the individual character size,
Expand Down Expand Up @@ -226,21 +224,15 @@ const LogViewer: React.FC<Props> = forwardRef((
// Add new logs to existing logs either at the beginning or the end.
const updatedLogs = prepend ? [ ...newLogs, ...logs ] : [ ...logs, ...newLogs ];
const logConfig = measureLogs(updatedLogs);
const isBottom = scroll.scrollTop >=
(scroll.scrollHeight - scroll.viewHeight - SCROLL_BOTTOM_THRESHOLD);

setConfig(logConfig);
setScrollToInfo({
isBottom,
isPrepend: prepend,
logId: logs[0]?.id,
});
setScrollToInfo({ isPrepend: prepend, logId: logs[0]?.id });
setLogs(updatedLogs);
setLogIdRange(prevLogIdRange => ({
max: Math.max(prevLogIdRange.max, newLogs[newLogs.length - 1].id),
min: Math.min(prevLogIdRange.min, newLogs[0].id),
}));
}, [ logs, logIdRange, measureLogs, scroll ]);
}, [ logs, logIdRange, measureLogs ]);

/*
* Figure out which logs lines to actually render based on whether it
Expand Down Expand Up @@ -288,6 +280,16 @@ const LogViewer: React.FC<Props> = forwardRef((
onScrollToTop(logs[0].id - 1);
}, [ logs, previousScroll, onScrollToTop, scroll ]);

/*
* Detect the user navigating away from the bottom to disengage
* the tailing behavior.
*/
useEffect(() => {
if (scroll.scrollTop < scroll.scrollHeight - scroll.viewHeight - SCROLL_BOTTOM_THRESHOLD) {
setIsTailing(false);
}
}, [ scroll ]);

/*
* Detect log viewer resize events to trigger
* recalculation of measured log entries.
Expand Down Expand Up @@ -330,7 +332,7 @@ const LogViewer: React.FC<Props> = forwardRef((
* user experience when scrolling through log entries.
*/
useLayoutEffect(() => {
if (scrollToInfo.isBottom) {
if (isTailing) {
/*
* Automatically scroll to the latest log entry if previously
* viewing the lastest log entry.
Expand All @@ -350,7 +352,7 @@ const LogViewer: React.FC<Props> = forwardRef((
container.current.scrollTo({ top });
});
}
}, [ config, scrollToInfo ]);
}, [ config, isTailing, scrollToInfo ]);

/*
* This overwrites the copy to clipboard event handler for the purpose of modifying the user
Expand Down Expand Up @@ -429,8 +431,9 @@ const LogViewer: React.FC<Props> = forwardRef((
container.current.scrollTo({ behavior: 'auto', top: 0 });
}, []);

const handleScrollToLatest = useCallback(() => {
const handleEnableTailing = useCallback(() => {
if (!container.current) return;
setIsTailing(true);
container.current.scrollTo({ behavior: 'auto', top: container.current.scrollHeight });
}, []);

Expand Down Expand Up @@ -516,12 +519,12 @@ const LogViewer: React.FC<Props> = forwardRef((
icon={<Icon name="arrow-up" />}
onClick={handleScrollToTop} />
</Tooltip>
<Tooltip placement="topRight" title="Scroll to Latest Entry">
<Tooltip placement="topRight" title={isTailing ? 'Tailing Enabled' : 'Enable Tailing'}>
<Button
aria-label="Scroll to Latest Entry"
className={scrollToLatestClasses.join(' ')}
aria-label="Enable Tailing"
className={enableTailingClasses.join(' ')}
icon={<Icon name="arrow-down" />}
onClick={handleScrollToLatest} />
onClick={handleEnableTailing} />
</Tooltip>
</div>
</Section>
Expand Down

0 comments on commit 3e59bd1

Please sign in to comment.