mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
csvviwer focus support (#46)
* more fixes * add delay and slight opacity animation * cleanup * focus support * use savedHeight as initial tbody height * cleanup * set container to savedHeight initially * fix calc issue * more calc fixes * more calc fixes
This commit is contained in:
parent
65db030126
commit
736fb7be6a
@ -6,6 +6,8 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@monaco-editor/react": "^4.5.1",
|
"@monaco-editor/react": "^4.5.1",
|
||||||
|
"@table-nav/core": "^0.0.7",
|
||||||
|
"@table-nav/react": "^0.0.7",
|
||||||
"@tanstack/match-sorter-utils": "^8.8.4",
|
"@tanstack/match-sorter-utils": "^8.8.4",
|
||||||
"@tanstack/react-table": "^8.10.3",
|
"@tanstack/react-table": "^8.10.3",
|
||||||
"autobind-decorator": "^2.4.0",
|
"autobind-decorator": "^2.4.0",
|
||||||
|
@ -21,10 +21,6 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.global-search-render {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.probe {
|
table.probe {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
@ -98,6 +94,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.csv-renderer.loaded {
|
.csv-renderer.show {
|
||||||
opacity: 1; /* When loaded class is added, set the opacity to 1, making it visible */
|
opacity: 1; /* When loaded class is added, set the opacity to 1, making it visible */
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,10 @@ import {
|
|||||||
flexRender,
|
flexRender,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
getFilteredRowModel,
|
|
||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
FilterFn,
|
FilterFn,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import { rankItem } from "@tanstack/match-sorter-utils";
|
import { useTableNav } from "@table-nav/react";
|
||||||
import SortUpIcon from "./img/sort-up-solid.svg";
|
import SortUpIcon from "./img/sort-up-solid.svg";
|
||||||
import SortDownIcon from "./img/sort-down-solid.svg";
|
import SortDownIcon from "./img/sort-down-solid.svg";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
@ -27,27 +26,16 @@ type CSVRow = {
|
|||||||
[key: string]: string | number;
|
[key: string]: string | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
|
|
||||||
// Rank the item
|
|
||||||
const itemRank = rankItem(row.getValue(columnId), value);
|
|
||||||
|
|
||||||
// Store the itemRank info
|
|
||||||
addMeta({
|
|
||||||
itemRank,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return if the item should be filtered in/out
|
|
||||||
return itemRank.passed;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: Blob;
|
data: Blob;
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
context: RendererContext;
|
context: RendererContext;
|
||||||
opts: RendererOpts;
|
opts: RendererOpts;
|
||||||
savedHeight: number;
|
savedHeight: number;
|
||||||
scrollToBringIntoViewport: () => void;
|
|
||||||
lineState: LineStateType;
|
lineState: LineStateType;
|
||||||
|
shouldFocus: boolean;
|
||||||
|
rendererApi: RendererModelContainerApi;
|
||||||
|
scrollToBringIntoViewport: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -59,7 +47,7 @@ interface State {
|
|||||||
const columnHelper = createColumnHelper<any>();
|
const columnHelper = createColumnHelper<any>();
|
||||||
|
|
||||||
const CSVRenderer: FC<Props> = (props: Props) => {
|
const CSVRenderer: FC<Props> = (props: Props) => {
|
||||||
const { data, opts, lineState, context, savedHeight } = props;
|
const { data, opts, lineState, context, shouldFocus, rendererApi, savedHeight } = props;
|
||||||
const { height: maxHeight } = opts.maxSize;
|
const { height: maxHeight } = opts.maxSize;
|
||||||
|
|
||||||
const csvCacheRef = useRef(new Map<string, string>());
|
const csvCacheRef = useRef(new Map<string, string>());
|
||||||
@ -70,11 +58,11 @@ const CSVRenderer: FC<Props> = (props: Props) => {
|
|||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
content: null,
|
content: null,
|
||||||
showReadonly: true,
|
showReadonly: true,
|
||||||
tbodyHeight: maxHeight,
|
tbodyHeight: 0,
|
||||||
});
|
});
|
||||||
const [globalFilter, setGlobalFilter] = useState("");
|
|
||||||
const [isFileTooLarge, setIsFileTooLarge] = useState<boolean>(false);
|
const [isFileTooLarge, setIsFileTooLarge] = useState<boolean>(false);
|
||||||
const [isRendererLoaded, setRendererLoaded] = useState(false);
|
const [tableLoaded, setTableLoaded] = useState(false);
|
||||||
|
const { listeners } = useTableNav();
|
||||||
|
|
||||||
const filePath = lineState["prompt:file"];
|
const filePath = lineState["prompt:file"];
|
||||||
const { screenId, lineId } = context;
|
const { screenId, lineId } = context;
|
||||||
@ -157,12 +145,12 @@ const CSVRenderer: FC<Props> = (props: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (probeRef.current && headerRef.current && parsedData.length) {
|
if (probeRef.current && headerRef.current && parsedData.length) {
|
||||||
const rowHeight = probeRef.current.offsetHeight;
|
const rowHeight = probeRef.current.offsetHeight;
|
||||||
const tbodyHeight = rowHeight * parsedData.length - rowHeight;
|
const fullTBodyHeight = rowHeight * parsedData.length;
|
||||||
const headerHeight = headerRef.current.offsetHeight; // For some reason, if we subtract this from maxHeight, the table is too short
|
const headerHeight = headerRef.current.offsetHeight;
|
||||||
const tbodyHeightLessHeader = tbodyHeight - headerHeight;
|
const maxHeightLessHeader = maxHeight - headerHeight;
|
||||||
const maxTbodyHeight = Math.min(maxHeight, tbodyHeightLessHeader);
|
const tbodyHeight = Math.min(maxHeightLessHeader, fullTBodyHeight);
|
||||||
|
|
||||||
setState((prevState) => ({ ...prevState, tbodyHeight: maxTbodyHeight }));
|
setState((prevState) => ({ ...prevState, tbodyHeight }));
|
||||||
}
|
}
|
||||||
}, [probeRef, headerRef, maxHeight, parsedData]);
|
}, [probeRef, headerRef, maxHeight, parsedData]);
|
||||||
|
|
||||||
@ -172,27 +160,24 @@ const CSVRenderer: FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
if (rowRef.current.length === parsedData.length) {
|
if (rowRef.current.length === parsedData.length) {
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
setRendererLoaded(true);
|
setTableLoaded(true);
|
||||||
}, 100); // Delay a bit to make sure the rows are rendered
|
}, 50); // Delay a bit to make sure the rows are rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [rowRef, parsedData]);
|
}, [rowRef, parsedData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shouldFocus) {
|
||||||
|
rendererApi.onFocusChanged(true);
|
||||||
|
}
|
||||||
|
}, [shouldFocus]);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
manualPagination: true,
|
manualPagination: true,
|
||||||
data: parsedData,
|
data: parsedData,
|
||||||
columns,
|
columns,
|
||||||
filterFns: {
|
|
||||||
fuzzy: fuzzyFilter,
|
|
||||||
},
|
|
||||||
state: {
|
|
||||||
globalFilter,
|
|
||||||
},
|
|
||||||
globalFilterFn: fuzzyFilter,
|
|
||||||
onGlobalFilterChange: setGlobalFilter,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -205,7 +190,10 @@ const CSVRenderer: FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("csv-renderer", { loaded: isRendererLoaded })}>
|
<div
|
||||||
|
className={cn("csv-renderer", { show: tableLoaded })}
|
||||||
|
style={{ height: tableLoaded ? "auto" : savedHeight }}
|
||||||
|
>
|
||||||
<table className="probe">
|
<table className="probe">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ref={probeRef}>
|
<tr ref={probeRef}>
|
||||||
@ -213,12 +201,18 @@ const CSVRenderer: FC<Props> = (props: Props) => {
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<table>
|
<table {...listeners}>
|
||||||
<thead>
|
<thead>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup, index) => (
|
||||||
<tr key={headerGroup.id} ref={headerRef}>
|
<tr key={headerGroup.id} ref={headerRef} id={headerGroup.id} tabIndex={index}>
|
||||||
{headerGroup.headers.map((header) => (
|
{headerGroup.headers.map((header, index) => (
|
||||||
<th key={header.id} colSpan={header.colSpan} style={{ width: header.getSize() }}>
|
<th
|
||||||
|
key={header.id}
|
||||||
|
colSpan={header.colSpan}
|
||||||
|
id={header.id}
|
||||||
|
tabIndex={index}
|
||||||
|
style={{ width: header.getSize() }}
|
||||||
|
>
|
||||||
{header.isPlaceholder ? null : (
|
{header.isPlaceholder ? null : (
|
||||||
<div
|
<div
|
||||||
{...{
|
{...{
|
||||||
@ -251,9 +245,11 @@ const CSVRenderer: FC<Props> = (props: Props) => {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody style={{ height: `${state.tbodyHeight}px` }} ref={tbodyRef}>
|
<tbody style={{ height: `${state.tbodyHeight}px` }} ref={tbodyRef}>
|
||||||
{table.getRowModel().rows.map((row, index) => (
|
{table.getRowModel().rows.map((row, index) => (
|
||||||
<tr key={row.id} ref={(el) => (rowRef.current[index] = el)}>
|
<tr key={row.id} ref={(el) => (rowRef.current[index] = el)} id={row.id} tabIndex={index}>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
<td key={cell.id} id={cell.id} tabIndex={index}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
// Copyright 2023, Command Line Inc.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
import React, { FC, useEffect, useState } from "react";
|
|
||||||
|
|
||||||
const DebouncedInput: FC<{
|
|
||||||
value: string | number;
|
|
||||||
onChange: (value: string | number) => void;
|
|
||||||
debounce?: number;
|
|
||||||
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>> = ({
|
|
||||||
value: initialValue,
|
|
||||||
onChange,
|
|
||||||
debounce = 500,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const [value, setValue] = useState(initialValue);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setValue(initialValue);
|
|
||||||
}, [initialValue]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
onChange(value);
|
|
||||||
}, debounce);
|
|
||||||
|
|
||||||
return () => clearTimeout(timeout);
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return <div className="search-renderer">
|
|
||||||
<input {...props} value={value} onChange={e => setValue(e.target.value)} />
|
|
||||||
</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DebouncedInput
|
|
10
yarn.lock
10
yarn.lock
@ -1641,6 +1641,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
defer-to-connect "^2.0.0"
|
defer-to-connect "^2.0.0"
|
||||||
|
|
||||||
|
"@table-nav/core@^0.0.7":
|
||||||
|
version "0.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@table-nav/core/-/core-0.0.7.tgz#2d7f58eb81aacf5bdd1b40a823dbef5a88792995"
|
||||||
|
integrity sha512-pCh18jHDRe3tw9sJZXfKi4cSD6VjHbn40CYdqhp5X91SIX7rakDEQAsTx6F7Fv9TUv265l+5rUDcYNaJ0N0cqQ==
|
||||||
|
|
||||||
|
"@table-nav/react@^0.0.7":
|
||||||
|
version "0.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@table-nav/react/-/react-0.0.7.tgz#a97699a490e31713fccd5f624ddb62e9fdc06a82"
|
||||||
|
integrity sha512-S+DsD/qDqp50Z4dqt5tZFMWA3sRu0OOT/grMQuq/z/52jPEKJB+b9t+YSH8Ms55vCJOJ0DxuYldJpYrJLMG5ew==
|
||||||
|
|
||||||
"@tanstack/match-sorter-utils@^8.8.4":
|
"@tanstack/match-sorter-utils@^8.8.4":
|
||||||
version "8.8.4"
|
version "8.8.4"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz#0b2864d8b7bac06a9f84cb903d405852cc40a457"
|
resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz#0b2864d8b7bac06a9f84cb903d405852cc40a457"
|
||||||
|
Loading…
Reference in New Issue
Block a user