1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Move LogRow to its own file

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Alex Andreev 2022-09-05 13:36:49 +03:00
parent da8570d974
commit f09974fd1f
4 changed files with 79 additions and 53 deletions

View File

@ -17,11 +17,11 @@
width: 100%;
font-family: var(--font-monospace);
font-size: smaller;
padding: 0 16px;
line-height: 120%;
> * {
-webkit-font-smoothing: auto; // Better readability on non-retina screens
padding: 2px 16px;
}
}

View File

@ -1,13 +1,10 @@
import styles from "./log-list.module.scss";
import { useVirtualizer } from '@tanstack/react-virtual';
import AnsiUp from 'ansi_up';
import DOMPurify from 'dompurify';
import { observer } from 'mobx-react';
import React, { useEffect, useRef } from 'react';
import { SearchStore } from '../../../search-store/search-store';
import { cssNames } from '../../../utils';
import type { LogTabViewModel } from './logs-view-model';
import { LogRow } from "./log-row";
export interface LogListProps {
model: LogTabViewModel;
@ -118,51 +115,3 @@ export const LogList = observer(({ model }: LogListProps) => {
)
});
const colorConverter = new AnsiUp();
function LogRow({ rowIndex, model }: { rowIndex: number; model: LogTabViewModel }) {
const { searchQuery, isActiveOverlay } = model.searchStore;
const log = model.visibleLogs.get()[rowIndex];
const contents: React.ReactElement[] = [];
const ansiToHtml = (ansi: string) => DOMPurify.sanitize(colorConverter.ansi_to_html(ansi));
if (searchQuery) { // If search is enabled, replace keyword with backgrounded <span>
// Case-insensitive search (lowercasing query and keywords in line)
const regex = new RegExp(SearchStore.escapeRegex(searchQuery), "gi");
const matches = log.matchAll(regex);
const modified = log.replace(regex, match => match.toLowerCase());
// Splitting text line by keyword
const pieces = modified.split(searchQuery.toLowerCase());
pieces.forEach((piece, index) => {
const active = isActiveOverlay(rowIndex, index);
const lastItem = index === pieces.length - 1;
const overlayValue = matches.next().value;
const overlay = !lastItem
? (
<span
className={cssNames("overlay", { active })}
dangerouslySetInnerHTML={{ __html: ansiToHtml(overlayValue) }}
/>
)
: null;
contents.push(
<React.Fragment key={piece + index}>
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(piece) }} />
{overlay}
</React.Fragment>,
);
});
}
return (
<div className={cssNames("LogRow")}>
{contents.length > 1 ? contents : (
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(log) }} />
)}
{/* For preserving copy-paste experience and keeping line breaks */}
<br />
</div>
);
}

View File

@ -0,0 +1,20 @@
.overlay {
--overlay-bg: #8cc474b8;
--overlay-active-bg: orange;
border-radius: 2px;
-webkit-font-smoothing: auto;
background-color: var(--overlay-bg);
span {
background-color: var(--overlay-bg)!important; // Rewriting inline styles from AnsiUp library
}
&.active {
background-color: var(--overlay-active-bg);
span {
background-color: var(--overlay-active-bg)!important; // Rewriting inline styles from AnsiUp library
}
}
}

View File

@ -0,0 +1,57 @@
import styles from "./log-row.module.scss";
import AnsiUp from 'ansi_up';
import DOMPurify from 'dompurify';
import React from 'react';
import { SearchStore } from '../../../search-store/search-store';
import { cssNames } from '../../../utils';
import type { LogTabViewModel } from './logs-view-model';
const colorConverter = new AnsiUp();
export function LogRow({ rowIndex, model }: { rowIndex: number; model: LogTabViewModel }) {
const { searchQuery, isActiveOverlay } = model.searchStore;
const log = model.visibleLogs.get()[rowIndex];
const contents: React.ReactElement[] = [];
const ansiToHtml = (ansi: string) => DOMPurify.sanitize(colorConverter.ansi_to_html(ansi));
if (searchQuery) { // If search is enabled, replace keyword with backgrounded <span>
// Case-insensitive search (lowercasing query and keywords in line)
const regex = new RegExp(SearchStore.escapeRegex(searchQuery), "gi");
const matches = log.matchAll(regex);
const modified = log.replace(regex, match => match.toLowerCase());
// Splitting text line by keyword
const pieces = modified.split(searchQuery.toLowerCase());
pieces.forEach((piece, index) => {
const active = isActiveOverlay(rowIndex, index);
const lastItem = index === pieces.length - 1;
const overlayValue = matches.next().value;
const overlay = !lastItem
? (
<span
className={cssNames(styles.overlay, { [styles.active]: active })}
dangerouslySetInnerHTML={{ __html: ansiToHtml(overlayValue) }}
/>
)
: null;
contents.push(
<React.Fragment key={piece + index}>
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(piece) }} />
{overlay}
</React.Fragment>,
);
});
}
return (
<div>
{contents.length > 1 ? contents : (
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(log) }} />
)}
{/* For preserving copy-paste experience and keeping line breaks */}
<br />
</div>
);
}