From 439833d300c03dfa1e7d50ac715133e6969173db Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 5 Dec 2021 11:29:09 +0100 Subject: [PATCH] core-components: split up LogViewer Signed-off-by: Patrik Oldsberg --- .../src/components/LogViewer/LogLine.tsx | 140 +++++++++++ .../src/components/LogViewer/LogViewer.tsx | 233 +----------------- .../src/components/LogViewer/styles.ts | 123 +++++++++ 3 files changed, 266 insertions(+), 230 deletions(-) create mode 100644 packages/core-components/src/components/LogViewer/LogLine.tsx create mode 100644 packages/core-components/src/components/LogViewer/styles.ts diff --git a/packages/core-components/src/components/LogViewer/LogLine.tsx b/packages/core-components/src/components/LogViewer/LogLine.tsx new file mode 100644 index 0000000000..7e5366eb77 --- /dev/null +++ b/packages/core-components/src/components/LogViewer/LogLine.tsx @@ -0,0 +1,140 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { AnsiLine, ChunkModifiers } from './AnsiProcessor'; +import startCase from 'lodash/startCase'; +import clsx from 'clsx'; +import { useStyles } from './useStyles'; + +function getModifierClasses( + classes: ReturnType, + modifiers: ChunkModifiers, +) { + const classNames = new Array(); + if (modifiers.bold) { + classNames.push(classes.modifierBold); + } + if (modifiers.italic) { + classNames.push(classes.modifierItalic); + } + if (modifiers.underline) { + classNames.push(classes.modifierUnderline); + } + if (modifiers.foreground) { + const key = `modifierForeground${startCase( + modifiers.foreground, + )}` as keyof typeof classes; + classNames.push(classes[key]); + } + if (modifiers.background) { + const key = `modifierBackground${startCase( + modifiers.background, + )}` as keyof typeof classes; + classNames.push(classes[key]); + } + return classNames.length > 0 ? classNames.join(' ') : undefined; +} + +export interface LogLineProps { + line: AnsiLine; + classes: ReturnType; + searchText: string; +} + +export function LogLine({ line, classes, searchText }: LogLineProps) { + let searchResults: Array<{ start: number; end: number }> | undefined = + undefined; + if (searchText && line.text.includes(searchText)) { + searchResults = []; + let offset = 0; + for (;;) { + const start = line.text.indexOf(searchText, offset); + if (start === -1) { + break; + } + const end = start + searchText.length; + searchResults.push({ start, end }); + offset = end; + } + } + + const output = new Array(line.chunks.length); + + let key = 0; + let chunkOffset = 0; + let nextResult = searchResults?.shift(); + for (const { text, modifiers } of line.chunks) { + if (!nextResult || chunkOffset + text.length < nextResult.start) { + output.push( + + {text} + , + ); + chunkOffset += text.length; + continue; + } + + let localOffset = 0; + while (nextResult) { + let localStart = nextResult.start - chunkOffset; + if (localStart < 0) { + localStart = 0; + } + const localEnd = nextResult.end - chunkOffset; + const beforeMatch = text.slice(localOffset, localStart); + const match = text.slice(localStart, localEnd); + + if (beforeMatch) { + output.push( + + {beforeMatch} + , + ); + } + output.push( + + {match} + , + ); + + localOffset = localStart + match.length; + + if (match.length === searchText.length) { + nextResult = searchResults?.shift(); + } else { + break; + } + } + + if (localOffset < text.length) { + output.push( + + {text.slice(localOffset)} + , + ); + } + + chunkOffset += text.length; + } + return <>{output}; +} diff --git a/packages/core-components/src/components/LogViewer/LogViewer.tsx b/packages/core-components/src/components/LogViewer/LogViewer.tsx index c1a5d06b34..d6722edbcd 100644 --- a/packages/core-components/src/components/LogViewer/LogViewer.tsx +++ b/packages/core-components/src/components/LogViewer/LogViewer.tsx @@ -14,17 +14,14 @@ * limitations under the License. */ -import { alpha, makeStyles } from '@material-ui/core/styles'; import React, { useMemo, useState } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; import { FixedSizeList } from 'react-window'; -import { AnsiChunk, AnsiLine, AnsiProcessor } from './AnsiProcessor'; -import startCase from 'lodash/startCase'; -import * as colors from '@material-ui/core/colors'; +import { AnsiProcessor } from './AnsiProcessor'; +import { HEADER_SIZE, useStyles } from './styles'; import clsx from 'clsx'; import TextField from '@material-ui/core/TextField'; - -const HEADER_SIZE = 40; +import { LogLine } from './LogLine'; export interface LogViewerProps { text: string; @@ -50,230 +47,6 @@ export interface ChunkModifiers { underline?: boolean; } -const useStyles = makeStyles(theme => ({ - root: { - background: theme.palette.background.paper, - }, - header: { - height: HEADER_SIZE, - display: 'flex', - alignItems: 'center', - justifyContent: 'flex-end', - }, - log: { - fontFamily: '"Monaco", monospace', - fontSize: theme.typography.fontSize, - }, - line: { - whiteSpace: 'pre', - - '&:hover': { - background: theme.palette.action.hover, - }, - }, - lineSelected: { - background: theme.palette.action.selected, - - '&:hover': { - background: theme.palette.action.selected, - }, - }, - lineNumber: { - display: 'inline-block', - textAlign: 'end', - width: 60, - marginRight: theme.spacing(1), - cursor: 'pointer', - }, - textHighlight: { - background: alpha(theme.palette.primary.main, 0.3), - }, - modifierBold: { - fontWeight: theme.typography.fontWeightBold, - }, - modifierItalic: { - fontStyle: 'italic', - }, - modifierUnderline: { - textDecoration: 'underline', - }, - modifierForegroundBlack: { - color: colors.common.black, - }, - modifierForegroundRed: { - color: colors.red[500], - }, - modifierForegroundGreen: { - color: colors.green[500], - }, - modifierForegroundYellow: { - color: colors.yellow[500], - }, - modifierForegroundBlue: { - color: colors.blue[500], - }, - modifierForegroundMagenta: { - color: colors.purple[500], - }, - modifierForegroundCyan: { - color: colors.cyan[500], - }, - modifierForegroundWhite: { - color: colors.common.white, - }, - modifierForegroundGrey: { - color: colors.grey[500], - }, - modifierBackgroundBlack: { - background: colors.common.black, - }, - modifierBackgroundRed: { - background: colors.red[500], - }, - modifierBackgroundGreen: { - background: colors.green[500], - }, - modifierBackgroundYellow: { - background: colors.yellow[500], - }, - modifierBackgroundBlue: { - background: colors.blue[500], - }, - modifierBackgroundMagenta: { - background: colors.purple[500], - }, - modifierBackgroundCyan: { - background: colors.cyan[500], - }, - modifierBackgroundWhite: { - background: colors.common.white, - }, - modifierBackgroundGrey: { - background: colors.grey[500], - }, -})); - -function getModifierClasses( - classes: ReturnType, - modifiers: ChunkModifiers, -) { - const classNames = new Array(); - if (modifiers.bold) { - classNames.push(classes.modifierBold); - } - if (modifiers.italic) { - classNames.push(classes.modifierItalic); - } - if (modifiers.underline) { - classNames.push(classes.modifierUnderline); - } - if (modifiers.foreground) { - const key = `modifierForeground${startCase( - modifiers.foreground, - )}` as keyof typeof classes; - classNames.push(classes[key]); - } - if (modifiers.background) { - const key = `modifierBackground${startCase( - modifiers.background, - )}` as keyof typeof classes; - classNames.push(classes[key]); - } - return classNames.length > 0 ? classNames.join(' ') : undefined; -} - -export function LogLine({ - line, - classes, - searchText, -}: { - line: AnsiLine; - classes: ReturnType; - searchText: string; -}) { - let searchResults: Array<{ start: number; end: number }> | undefined = - undefined; - if (searchText && line.text.includes(searchText)) { - searchResults = []; - let offset = 0; - for (;;) { - const start = line.text.indexOf(searchText, offset); - if (start === -1) { - break; - } - const end = start + searchText.length; - searchResults.push({ start, end }); - offset = end; - } - } - - const output = new Array(line.chunks.length); - - let key = 0; - let chunkOffset = 0; - let nextResult = searchResults?.shift(); - for (const { text, modifiers } of line.chunks) { - if (!nextResult || chunkOffset + text.length < nextResult.start) { - output.push( - - {text} - , - ); - chunkOffset += text.length; - continue; - } - - let localOffset = 0; - while (nextResult) { - let localStart = nextResult.start - chunkOffset; - if (localStart < 0) { - localStart = 0; - } - const localEnd = nextResult.end - chunkOffset; - const beforeMatch = text.slice(localOffset, localStart); - const match = text.slice(localStart, localEnd); - - if (beforeMatch) { - output.push( - - {beforeMatch} - , - ); - } - output.push( - - {match} - , - ); - - localOffset = localStart + match.length; - - if (match.length === searchText.length) { - nextResult = searchResults?.shift(); - } else { - break; - } - } - - if (localOffset < text.length) { - output.push( - - {text.slice(localOffset)} - , - ); - } - - chunkOffset += text.length; - } - return <>{output}; -} - export function LogViewer(props: LogViewerProps) { const { noLineNumbers } = props; const classes = useStyles(); diff --git a/packages/core-components/src/components/LogViewer/styles.ts b/packages/core-components/src/components/LogViewer/styles.ts new file mode 100644 index 0000000000..fd66571915 --- /dev/null +++ b/packages/core-components/src/components/LogViewer/styles.ts @@ -0,0 +1,123 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { alpha, makeStyles } from '@material-ui/core/styles'; +import * as colors from '@material-ui/core/colors'; + +export const HEADER_SIZE = 40; + +export const useStyles = makeStyles(theme => ({ + root: { + background: theme.palette.background.paper, + }, + header: { + height: HEADER_SIZE, + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + }, + log: { + fontFamily: '"Monaco", monospace', + fontSize: theme.typography.fontSize, + }, + line: { + whiteSpace: 'pre', + + '&:hover': { + background: theme.palette.action.hover, + }, + }, + lineSelected: { + background: theme.palette.action.selected, + + '&:hover': { + background: theme.palette.action.selected, + }, + }, + lineNumber: { + display: 'inline-block', + textAlign: 'end', + width: 60, + marginRight: theme.spacing(1), + cursor: 'pointer', + }, + textHighlight: { + background: alpha(theme.palette.primary.main, 0.3), + }, + modifierBold: { + fontWeight: theme.typography.fontWeightBold, + }, + modifierItalic: { + fontStyle: 'italic', + }, + modifierUnderline: { + textDecoration: 'underline', + }, + modifierForegroundBlack: { + color: colors.common.black, + }, + modifierForegroundRed: { + color: colors.red[500], + }, + modifierForegroundGreen: { + color: colors.green[500], + }, + modifierForegroundYellow: { + color: colors.yellow[500], + }, + modifierForegroundBlue: { + color: colors.blue[500], + }, + modifierForegroundMagenta: { + color: colors.purple[500], + }, + modifierForegroundCyan: { + color: colors.cyan[500], + }, + modifierForegroundWhite: { + color: colors.common.white, + }, + modifierForegroundGrey: { + color: colors.grey[500], + }, + modifierBackgroundBlack: { + background: colors.common.black, + }, + modifierBackgroundRed: { + background: colors.red[500], + }, + modifierBackgroundGreen: { + background: colors.green[500], + }, + modifierBackgroundYellow: { + background: colors.yellow[500], + }, + modifierBackgroundBlue: { + background: colors.blue[500], + }, + modifierBackgroundMagenta: { + background: colors.purple[500], + }, + modifierBackgroundCyan: { + background: colors.cyan[500], + }, + modifierBackgroundWhite: { + background: colors.common.white, + }, + modifierBackgroundGrey: { + background: colors.grey[500], + }, +}));