mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
CSV view (#73)
This commit is contained in:
parent
4714b88be7
commit
edb8eb25b8
99
frontend/app/view/csvview.less
Normal file
99
frontend/app/view/csvview.less
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
.csv-view {
|
||||||
|
opacity: 0; /* Start with an opacity of 0, meaning it's invisible */
|
||||||
|
|
||||||
|
.ellipsis() {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-none {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.probe {
|
||||||
|
position: absolute;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
overflow-x: auto;
|
||||||
|
border: 1px solid var(--scrollbar-thumb-hover-color);
|
||||||
|
|
||||||
|
thead {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border-bottom: 1px solid var(--scrollbar-thumb-hover-color);
|
||||||
|
|
||||||
|
th {
|
||||||
|
color: var(--app-text-color);
|
||||||
|
border-right: 1px solid var(--scrollbar-thumb-hover-color);
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 2px 10px;
|
||||||
|
flex-basis: 100%;
|
||||||
|
flex-grow: 2;
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
text-align: left;
|
||||||
|
padding-right: 15px;
|
||||||
|
position: relative;
|
||||||
|
.ellipsis();
|
||||||
|
|
||||||
|
.sort-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 2px;
|
||||||
|
width: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
td {
|
||||||
|
border-right: 1px solid var(--scrollbar-thumb-hover-color);
|
||||||
|
border-left: 1px solid var(--scrollbar-thumb-hover-color);
|
||||||
|
padding: 3px 10px;
|
||||||
|
flex-basis: 100%;
|
||||||
|
flex-grow: 2;
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
.ellipsis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.csv-view.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
205
frontend/app/view/csvview.tsx
Normal file
205
frontend/app/view/csvview.tsx
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import { useTableNav } from "@table-nav/react";
|
||||||
|
import {
|
||||||
|
createColumnHelper,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { clsx } from "clsx";
|
||||||
|
import Papa from "papaparse";
|
||||||
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
|
import "./csvview.less";
|
||||||
|
|
||||||
|
const MAX_DATA_SIZE = 10 * 1024 * 1024; // 10MB in bytes
|
||||||
|
|
||||||
|
type CSVRow = {
|
||||||
|
[key: string]: string | number;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface CSVViewProps {
|
||||||
|
parentRef: React.MutableRefObject<HTMLDivElement>;
|
||||||
|
content: string;
|
||||||
|
filename: string;
|
||||||
|
readonly: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
content: string | null;
|
||||||
|
showReadonly: boolean;
|
||||||
|
tbodyHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<any>();
|
||||||
|
|
||||||
|
const CSVView = ({ parentRef, filename, content }: CSVViewProps) => {
|
||||||
|
const csvCacheRef = useRef(new Map<string, string>());
|
||||||
|
const rowRef = useRef<(HTMLTableRowElement | null)[]>([]);
|
||||||
|
const headerRef = useRef<HTMLTableRowElement | null>(null);
|
||||||
|
const probeRef = useRef<HTMLTableRowElement | null>(null);
|
||||||
|
const tbodyRef = useRef<HTMLTableSectionElement | null>(null);
|
||||||
|
const [state, setState] = useState<State>({
|
||||||
|
content,
|
||||||
|
showReadonly: true,
|
||||||
|
tbodyHeight: 0,
|
||||||
|
});
|
||||||
|
const [tableLoaded, setTableLoaded] = useState(false);
|
||||||
|
const [maxHeight, setMaxHeight] = useState(0);
|
||||||
|
const { listeners } = useTableNav();
|
||||||
|
|
||||||
|
const cacheKey = `${filename}`;
|
||||||
|
csvCacheRef.current.set(cacheKey, content);
|
||||||
|
|
||||||
|
// Parse the CSV data
|
||||||
|
const parsedData = useMemo<CSVRow[]>(() => {
|
||||||
|
if (!state.content) return [];
|
||||||
|
|
||||||
|
// Trim the content and then check for headers based on the first row's content.
|
||||||
|
const trimmedContent = state.content.trim();
|
||||||
|
const firstRow = trimmedContent.split("\n")[0];
|
||||||
|
|
||||||
|
// This checks if the first row starts with a letter or a quote
|
||||||
|
const hasHeaders = !!firstRow.match(/^[a-zA-Z"]/);
|
||||||
|
|
||||||
|
const results = Papa.parse(trimmedContent, { header: hasHeaders });
|
||||||
|
|
||||||
|
// Check for non-header CSVs
|
||||||
|
if (!hasHeaders && Array.isArray(results.data) && Array.isArray(results.data[0])) {
|
||||||
|
const dataArray = results.data as string[][]; // Asserting the type
|
||||||
|
const headers = Array.from({ length: dataArray[0].length }, (_, i) => `Column ${i + 1}`);
|
||||||
|
results.data = dataArray.map((row) => {
|
||||||
|
const newRow: CSVRow = {};
|
||||||
|
row.forEach((value, index) => {
|
||||||
|
newRow[headers[index]] = value;
|
||||||
|
});
|
||||||
|
return newRow;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.data.map((row) => {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(row as CSVRow).map(([key, value]) => {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
const numberValue = parseFloat(value);
|
||||||
|
if (!isNaN(numberValue) && String(numberValue) === value) {
|
||||||
|
return [key, numberValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [key, value];
|
||||||
|
})
|
||||||
|
) as CSVRow;
|
||||||
|
});
|
||||||
|
}, [state.content]);
|
||||||
|
|
||||||
|
// Column Definitions
|
||||||
|
const columns = useMemo(() => {
|
||||||
|
if (parsedData.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const headers = Object.keys(parsedData[0]);
|
||||||
|
return headers.map((header) =>
|
||||||
|
columnHelper.accessor(header, {
|
||||||
|
header: () => header,
|
||||||
|
cell: (info) => info.renderValue(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [parsedData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (probeRef.current && headerRef.current && parsedData.length && parentRef.current) {
|
||||||
|
const rowHeight = probeRef.current.offsetHeight;
|
||||||
|
const fullTBodyHeight = rowHeight * parsedData.length;
|
||||||
|
const headerHeight = headerRef.current.offsetHeight;
|
||||||
|
const maxHeight = parentRef.current.getBoundingClientRect().height - 32; // 32 is the border plus the breadcrumb height
|
||||||
|
const maxHeightLessHeader = maxHeight - headerHeight;
|
||||||
|
const tbodyHeight = Math.min(maxHeightLessHeader, fullTBodyHeight);
|
||||||
|
|
||||||
|
setState((prevState) => ({ ...prevState, tbodyHeight }));
|
||||||
|
}
|
||||||
|
}, [maxHeight, parsedData]);
|
||||||
|
|
||||||
|
// Makes sure rows are rendered before setting the renderer as loaded
|
||||||
|
useEffect(() => {
|
||||||
|
let tid: NodeJS.Timeout;
|
||||||
|
|
||||||
|
if (rowRef.current.length === parsedData.length) {
|
||||||
|
tid = setTimeout(() => {
|
||||||
|
setTableLoaded(true);
|
||||||
|
}, 50); // Delay a bit to make sure the rows are rendered
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => clearTimeout(tid);
|
||||||
|
}, [rowRef, parsedData]);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
manualPagination: true,
|
||||||
|
data: parsedData,
|
||||||
|
columns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx("csv-view", { show: tableLoaded })} style={{ height: "auto" }}>
|
||||||
|
<table className="probe">
|
||||||
|
<tbody>
|
||||||
|
<tr ref={probeRef}>
|
||||||
|
<td>dummy data</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table {...listeners}>
|
||||||
|
<thead>
|
||||||
|
{table.getHeaderGroups().map((headerGroup, index) => (
|
||||||
|
<tr key={headerGroup.id} ref={headerRef} id={headerGroup.id} tabIndex={index}>
|
||||||
|
{headerGroup.headers.map((header, index) => (
|
||||||
|
<th
|
||||||
|
key={header.id}
|
||||||
|
colSpan={header.colSpan}
|
||||||
|
id={header.id}
|
||||||
|
tabIndex={index}
|
||||||
|
style={{ width: header.getSize() }}
|
||||||
|
>
|
||||||
|
{header.isPlaceholder ? null : (
|
||||||
|
<div
|
||||||
|
{...{
|
||||||
|
className: header.column.getCanSort()
|
||||||
|
? "inner cursor-pointer select-none"
|
||||||
|
: "",
|
||||||
|
onClick: header.column.getToggleSortingHandler(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
{header.column.getIsSorted() === "asc" ? (
|
||||||
|
<i className="sort-icon fa-sharp fa-solid fa-sort-up"></i>
|
||||||
|
) : header.column.getIsSorted() === "desc" ? (
|
||||||
|
<i className="sort-icon fa-sharp fa-solid fa-sort-down"></i>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody style={{ height: `${state.tbodyHeight}px` }} ref={tbodyRef}>
|
||||||
|
{table.getRowModel().rows.map((row, index) => (
|
||||||
|
<tr key={row.id} ref={(el) => (rowRef.current[index] = el)} id={row.id} tabIndex={index}>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<td key={cell.id} id={cell.id} tabIndex={index}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { CSVView };
|
@ -8,8 +8,10 @@ import * as WOS from "@/store/wos";
|
|||||||
import * as util from "@/util/util";
|
import * as util from "@/util/util";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
|
import { useRef } from "react";
|
||||||
import { CenteredDiv } from "../element/quickelems";
|
import { CenteredDiv } from "../element/quickelems";
|
||||||
import { CodeEdit } from "./codeedit";
|
import { CodeEdit } from "./codeedit";
|
||||||
|
import { CSVView } from "./csvview";
|
||||||
import { DirectoryPreview } from "./directorypreview";
|
import { DirectoryPreview } from "./directorypreview";
|
||||||
|
|
||||||
import "./view.less";
|
import "./view.less";
|
||||||
@ -121,6 +123,21 @@ function CodeEditPreview({
|
|||||||
return <CodeEdit readonly={true} text={fileContent} filename={filename} />;
|
return <CodeEdit readonly={true} text={fileContent} filename={filename} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CSVViewPreview({
|
||||||
|
parentRef,
|
||||||
|
contentAtom,
|
||||||
|
filename,
|
||||||
|
readonly,
|
||||||
|
}: {
|
||||||
|
parentRef: React.MutableRefObject<HTMLDivElement>;
|
||||||
|
contentAtom: jotai.Atom<Promise<string>>;
|
||||||
|
filename: string;
|
||||||
|
readonly: boolean;
|
||||||
|
}) {
|
||||||
|
const fileContent = jotai.useAtomValue(contentAtom);
|
||||||
|
return <CSVView parentRef={parentRef} readonly={true} content={fileContent} filename={filename} />;
|
||||||
|
}
|
||||||
|
|
||||||
function iconForFile(mimeType: string, fileName: string): string {
|
function iconForFile(mimeType: string, fileName: string): string {
|
||||||
if (mimeType == "application/pdf") {
|
if (mimeType == "application/pdf") {
|
||||||
return "file-pdf";
|
return "file-pdf";
|
||||||
@ -149,6 +166,7 @@ function iconForFile(mimeType: string, fileName: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function PreviewView({ blockId }: { blockId: string }) {
|
function PreviewView({ blockId }: { blockId: string }) {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
const blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
||||||
const fileNameAtom: jotai.WritableAtom<string, [string], void> = useBlockCache(blockId, "preview:filename", () =>
|
const fileNameAtom: jotai.WritableAtom<string, [string], void> = useBlockCache(blockId, "preview:filename", () =>
|
||||||
jotai.atom<string, [string], void>(
|
jotai.atom<string, [string], void>(
|
||||||
@ -220,6 +238,10 @@ function PreviewView({ blockId }: { blockId: string }) {
|
|||||||
specializedView = <CenteredDiv>File Too Large to Preview</CenteredDiv>;
|
specializedView = <CenteredDiv>File Too Large to Preview</CenteredDiv>;
|
||||||
} else if (mimeType === "text/markdown") {
|
} else if (mimeType === "text/markdown") {
|
||||||
specializedView = <MarkdownPreview contentAtom={fileContentAtom} />;
|
specializedView = <MarkdownPreview contentAtom={fileContentAtom} />;
|
||||||
|
} else if (mimeType === "text/csv") {
|
||||||
|
specializedView = (
|
||||||
|
<CSVViewPreview parentRef={ref} contentAtom={fileContentAtom} filename={fileName} readonly={true} />
|
||||||
|
);
|
||||||
} else if (
|
} else if (
|
||||||
mimeType.startsWith("text/") ||
|
mimeType.startsWith("text/") ||
|
||||||
(mimeType.startsWith("application/") &&
|
(mimeType.startsWith("application/") &&
|
||||||
@ -243,7 +265,7 @@ function PreviewView({ blockId }: { blockId: string }) {
|
|||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="full-preview">
|
<div ref={ref} className="full-preview">
|
||||||
<DirNav cwdAtom={fileNameAtom} />
|
<DirNav cwdAtom={fileNameAtom} />
|
||||||
{specializedView}
|
{specializedView}
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,7 +98,6 @@ function WorkspaceElem() {
|
|||||||
const windowData = jotai.useAtomValue(atoms.waveWindow);
|
const windowData = jotai.useAtomValue(atoms.waveWindow);
|
||||||
const activeTabId = windowData?.activetabid;
|
const activeTabId = windowData?.activetabid;
|
||||||
const ws = jotai.useAtomValue(atoms.workspace);
|
const ws = jotai.useAtomValue(atoms.workspace);
|
||||||
console.log("ws", ws);
|
|
||||||
return (
|
return (
|
||||||
<div className="workspace">
|
<div className="workspace">
|
||||||
<TabBar workspace={ws} />
|
<TabBar workspace={ws} />
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"@storybook/react-vite": "^8.1.9",
|
"@storybook/react-vite": "^8.1.9",
|
||||||
"@storybook/test": "^8.1.9",
|
"@storybook/test": "^8.1.9",
|
||||||
"@types/node": "^20.12.12",
|
"@types/node": "^20.12.12",
|
||||||
|
"@types/papaparse": "^5",
|
||||||
"@types/react": "^18.3.2",
|
"@types/react": "^18.3.2",
|
||||||
"@types/throttle-debounce": "^5",
|
"@types/throttle-debounce": "^5",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
@ -53,6 +54,8 @@
|
|||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@observablehq/plot": "^0.6.14",
|
"@observablehq/plot": "^0.6.14",
|
||||||
"@react-hook/resize-observer": "^2.0.1",
|
"@react-hook/resize-observer": "^2.0.1",
|
||||||
|
"@table-nav/core": "^0.0.7",
|
||||||
|
"@table-nav/react": "^0.0.7",
|
||||||
"@tanstack/react-table": "^8.17.3",
|
"@tanstack/react-table": "^8.17.3",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/addon-serialize": "^0.13.0",
|
"@xterm/addon-serialize": "^0.13.0",
|
||||||
@ -66,6 +69,7 @@
|
|||||||
"jotai": "^2.8.0",
|
"jotai": "^2.8.0",
|
||||||
"monaco-editor": "^0.49.0",
|
"monaco-editor": "^0.49.0",
|
||||||
"overlayscrollbars": "^2.8.3",
|
"overlayscrollbars": "^2.8.3",
|
||||||
|
"papaparse": "^5.4.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
|
36
yarn.lock
36
yarn.lock
@ -3889,6 +3889,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@table-nav/core@npm:^0.0.7":
|
||||||
|
version: 0.0.7
|
||||||
|
resolution: "@table-nav/core@npm:0.0.7"
|
||||||
|
checksum: 10c0/75955f8ed2c0beef56bfe9dcf9ee0f24d593a49eaa364edbf0d32023242cb39172855d390838c8e818dbed5aa3bf0891151d973bc203fd2f18b3e5072daf97aa
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@table-nav/react@npm:^0.0.7":
|
||||||
|
version: 0.0.7
|
||||||
|
resolution: "@table-nav/react@npm:0.0.7"
|
||||||
|
peerDependencies:
|
||||||
|
"@table-nav/core": ^0.0.7
|
||||||
|
checksum: 10c0/a03baf6fb38bd92260823f15f8309f31fdffec72d2ef43e4d8c808c0aa2081e9e4147f675e961270fa676bcc8361388c08d9dfbaad14458d5cd2b22e76de39f0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@tanstack/react-table@npm:^8.17.3":
|
"@tanstack/react-table@npm:^8.17.3":
|
||||||
version: 8.17.3
|
version: 8.17.3
|
||||||
resolution: "@tanstack/react-table@npm:8.17.3"
|
resolution: "@tanstack/react-table@npm:8.17.3"
|
||||||
@ -4323,6 +4339,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/papaparse@npm:^5":
|
||||||
|
version: 5.3.14
|
||||||
|
resolution: "@types/papaparse@npm:5.3.14"
|
||||||
|
dependencies:
|
||||||
|
"@types/node": "npm:*"
|
||||||
|
checksum: 10c0/feb4d215903b67442feaa9836a6a5771e78dc6a9da24781e399c6f891622fa82245cd783ab2613c5be43e4a2d6a94da52325538e4485af258166864576ecd0d8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/pretty-hrtime@npm:^1.0.0":
|
"@types/pretty-hrtime@npm:^1.0.0":
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
resolution: "@types/pretty-hrtime@npm:1.0.3"
|
resolution: "@types/pretty-hrtime@npm:1.0.3"
|
||||||
@ -10413,6 +10438,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"papaparse@npm:^5.4.1":
|
||||||
|
version: 5.4.1
|
||||||
|
resolution: "papaparse@npm:5.4.1"
|
||||||
|
checksum: 10c0/201f37c4813453fed5bfb4c01816696b099d2db9ff1e8fb610acc4771fdde91d2a22b6094721edb0fedb21ca3c46f04263f68be4beb3e35b8c72278f0cedc7b7
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"parent-module@npm:^1.0.0":
|
"parent-module@npm:^1.0.0":
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
resolution: "parent-module@npm:1.0.1"
|
resolution: "parent-module@npm:1.0.1"
|
||||||
@ -12288,8 +12320,11 @@ __metadata:
|
|||||||
"@storybook/react": "npm:^8.1.9"
|
"@storybook/react": "npm:^8.1.9"
|
||||||
"@storybook/react-vite": "npm:^8.1.9"
|
"@storybook/react-vite": "npm:^8.1.9"
|
||||||
"@storybook/test": "npm:^8.1.9"
|
"@storybook/test": "npm:^8.1.9"
|
||||||
|
"@table-nav/core": "npm:^0.0.7"
|
||||||
|
"@table-nav/react": "npm:^0.0.7"
|
||||||
"@tanstack/react-table": "npm:^8.17.3"
|
"@tanstack/react-table": "npm:^8.17.3"
|
||||||
"@types/node": "npm:^20.12.12"
|
"@types/node": "npm:^20.12.12"
|
||||||
|
"@types/papaparse": "npm:^5"
|
||||||
"@types/react": "npm:^18.3.2"
|
"@types/react": "npm:^18.3.2"
|
||||||
"@types/throttle-debounce": "npm:^5"
|
"@types/throttle-debounce": "npm:^5"
|
||||||
"@types/uuid": "npm:^9.0.8"
|
"@types/uuid": "npm:^9.0.8"
|
||||||
@ -12311,6 +12346,7 @@ __metadata:
|
|||||||
less: "npm:^4.2.0"
|
less: "npm:^4.2.0"
|
||||||
monaco-editor: "npm:^0.49.0"
|
monaco-editor: "npm:^0.49.0"
|
||||||
overlayscrollbars: "npm:^2.8.3"
|
overlayscrollbars: "npm:^2.8.3"
|
||||||
|
papaparse: "npm:^5.4.1"
|
||||||
prettier: "npm:^3.2.5"
|
prettier: "npm:^3.2.5"
|
||||||
prettier-plugin-jsdoc: "npm:^1.3.0"
|
prettier-plugin-jsdoc: "npm:^1.3.0"
|
||||||
prettier-plugin-organize-imports: "npm:^3.2.4"
|
prettier-plugin-organize-imports: "npm:^3.2.4"
|
||||||
|
Loading…
Reference in New Issue
Block a user