mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Create functional LogList component
Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
parent
905af0b3a5
commit
da39984cc1
108
src/renderer/components/dock/logs/log-list.tsx
Normal file
108
src/renderer/components/dock/logs/log-list.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import AnsiUp from 'ansi_up';
|
||||
import DOMPurify from 'dompurify';
|
||||
import React, { useRef } from 'react';
|
||||
import { SearchStore } from '../../../search-store/search-store';
|
||||
import { cssNames } from '../../../utils';
|
||||
import type { LogTabViewModel } from './logs-view-model';
|
||||
|
||||
export interface LogListProps {
|
||||
model: LogTabViewModel;
|
||||
}
|
||||
|
||||
export const LogList = ({ model }: LogListProps) => {
|
||||
const { logs } = model;
|
||||
const parentRef = useRef<HTMLDivElement>(null)
|
||||
const rowVirtualizer = useVirtualizer({
|
||||
count: logs.get().length,
|
||||
getScrollElement: () => parentRef.current,
|
||||
estimateSize: () => 38,
|
||||
overscan: 5
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={parentRef}
|
||||
style={{
|
||||
height: `400px`,
|
||||
overflow: 'auto', // Make it scroll!
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: `${rowVirtualizer.getTotalSize()}px`,
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
|
||||
<div
|
||||
key={virtualRow.index}
|
||||
ref={virtualRow.measureElement}
|
||||
className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<LogRow rowIndex={virtualRow.index} model={model} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const colorConverter = new AnsiUp();
|
||||
|
||||
function LogRow({ rowIndex, model }: { rowIndex: number; model: LogTabViewModel }) {
|
||||
const { searchQuery, isActiveOverlay } = model.searchStore;
|
||||
const log = model.logs.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>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user