diff --git a/package.json b/package.json index b0ca147..927ff42 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@reduxjs/toolkit": "^1.7.1", "@rematch/core": "^2.2.0", "@rematch/immer": "^2.1.3", + "@types/react-virtualized": "^9.21.16", "apache-arrow": "^6.0.1", "chakra-react-select": "^1.3.2", "compassql": "^0.21.2", @@ -37,6 +38,7 @@ "react-instantsearch-dom": "^6.15.0", "react-redux": "^7.2.6", "react-vega": "^7.4.4", + "react-virtualized": "^9.22.3", "redux": "^4.1.2", "reselect": "^4.1.4", "sqlstring": "^2.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8b8fbc..fb42aae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,7 @@ specifiers: '@types/react': ^17.0.35 '@types/react-dom': ^17.0.11 '@types/react-instantsearch-core': ^6.10.5 + '@types/react-virtualized': ^9.21.16 apache-arrow: ^6.0.1 chakra-react-select: ^1.3.2 compassql: ^0.21.2 @@ -35,6 +36,7 @@ specifiers: react-instantsearch-dom: ^6.15.0 react-redux: ^7.2.6 react-vega: ^7.4.4 + react-virtualized: ^9.22.3 redux: ^4.1.2 reselect: ^4.1.4 sqlstring: ^2.3.2 @@ -57,6 +59,7 @@ dependencies: '@reduxjs/toolkit': 1.7.1_react-redux@7.2.6+react@17.0.2 '@rematch/core': 2.2.0_redux@4.1.2 '@rematch/immer': 2.1.3_ed1556fc8f632794cbf1d2aaf9845db1 + '@types/react-virtualized': 9.21.16 apache-arrow: 6.0.1 chakra-react-select: 1.3.2_e7d2d323dd6853664c50a66f3b0bb50f compassql: 0.21.2_vega@5.21.0 @@ -75,6 +78,7 @@ dependencies: react-instantsearch-dom: 6.15.0_react-dom@17.0.2+react@17.0.2 react-redux: 7.2.6_react-dom@17.0.2+react@17.0.2 react-vega: 7.4.4_4073cc14f0b3a4b719b0071b64030d94 + react-virtualized: 9.22.3_react-dom@17.0.2+react@17.0.2 redux: 4.1.2 reselect: 4.1.4 sqlstring: 2.3.2 @@ -449,7 +453,7 @@ packages: resolution: {integrity: sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==} engines: {node: '>=6.9.0'} dependencies: - regenerator-runtime: 0.13.4 + regenerator-runtime: 0.13.9 dev: false /@babel/runtime/7.16.3: @@ -2055,6 +2059,13 @@ packages: '@types/react': 17.0.35 dev: false + /@types/react-virtualized/9.21.16: + resolution: {integrity: sha512-25QMrouz1VKq5T68+9JfRq3GP4JmN20ARi+hl5z7NMmriy07XmGd1Yh3I4EIZjmifpXhHZoQS/ny2fzZRyiAiw==} + dependencies: + '@types/prop-types': 15.7.4 + '@types/react': 17.0.35 + dev: false + /@types/react/16.14.21: resolution: {integrity: sha512-rY4DzPKK/4aohyWiDRHS2fotN5rhBSK6/rz1X37KzNna9HJyqtaGAbq9fVttrEPWF5ywpfIP1ITL8Xi2QZn6Eg==} dependencies: @@ -2656,6 +2667,11 @@ packages: engines: {node: '>=0.8'} dev: false + /clsx/1.1.1: + resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==} + engines: {node: '>=6'} + dev: false + /code-point-at/1.1.0: resolution: {integrity: sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=} engines: {node: '>=0.10.0'} @@ -5274,6 +5290,10 @@ packages: /react-is/17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + /react-lifecycles-compat/3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + dev: false + /react-redux/7.2.6_react-dom@17.0.2+react@17.0.2: resolution: {integrity: sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==} peerDependencies: @@ -5402,6 +5422,22 @@ packages: vega-lite: 5.0.0_vega@5.21.0 dev: false + /react-virtualized/9.22.3_react-dom@17.0.2+react@17.0.2: + resolution: {integrity: sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==} + peerDependencies: + react: ^15.3.0 || ^16.0.0-alpha + react-dom: ^15.3.0 || ^16.0.0-alpha + dependencies: + '@babel/runtime': 7.16.3 + clsx: 1.1.1 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.7.2 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-lifecycles-compat: 3.0.4 + dev: false + /react/17.0.2: resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} engines: {node: '>=0.10.0'} diff --git a/src/features/sqlEditor/components/TableGrid.tsx b/src/features/sqlEditor/components/TableGrid.tsx new file mode 100644 index 0000000..90bb08b --- /dev/null +++ b/src/features/sqlEditor/components/TableGrid.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import { useAppSelector } from "hooks"; +import { ArrowTableGrid } from "./arrow-viewer/ArrowTableGrid"; + +export const TableGrid = () => { + const data = useAppSelector((s) => s.sqlQuery.data); + const status = useAppSelector((s) => s.sqlQuery.status); + return status == "completed" ? ( + + ) : null; +}; diff --git a/src/features/sqlEditor/components/arrow-viewer/ArrowTableGrid.tsx b/src/features/sqlEditor/components/arrow-viewer/ArrowTableGrid.tsx new file mode 100644 index 0000000..df5b033 --- /dev/null +++ b/src/features/sqlEditor/components/arrow-viewer/ArrowTableGrid.tsx @@ -0,0 +1,101 @@ +// From github.com/cwharris/arrow-viewer +import {Table as ArrowTable} from "apache-arrow"; + +import { valueToString } from "utils/arrow-utils" + +import { Table, Column, AutoSizer, Index } from "react-virtualized"; + +import "react-virtualized/styles.css"; +import { defaultRowRenderer, TableCellProps, TableHeaderProps, TableHeaderRowProps, TableRowProps } from "react-virtualized/dist/es/Table"; + +export function ArrowTableGrid({ + table, + width, + height, +}: { + table: ArrowTable; + width: number; + height: number; +}): JSX.Element { + return ( + + {(size) => ( + table.get(index)} + rowRenderer={(props: TableRowProps) => { + // console.log(props); + + return defaultRowRenderer({ + ...props, + style: { ...props.style, width: props.style.width - 15 }, + }); + }} + headerStyle={headerStyle()} + headerRowRenderer={({ + className, + columns, + style, + }: TableHeaderRowProps) => ( +
+ {columns} +
+ )} + > + {table.schema.fields.map((field, idx) => { + // console.log(field, idx); + + return ( + { + // console.log(props); + const d = props.rowData[idx]; + // console.log(d); + return d; + }} + cellRenderer={({ cellData }: TableCellProps) => + valueToString(cellData) + } + headerRenderer={({ + label, + }: TableHeaderProps) => label} + /> + ); + })} +
+ )} +
+ ); +} + +const headerStyle = () => + ({ textAlign: "right", textTransform: "none" } as React.CSSProperties); + +const rowStyle = ({ index }: Index) => + index % 2 === 0 + ? { + ...headerStyle(), + borderBottom: "1px solid #e0e0e0", + backgroundColor: "#fff", + } + : { + ...headerStyle(), + borderBottom: "1px solid #e0e0e0", + backgroundColor: "#fafafa", + }; diff --git a/src/features/workspace/components/Workspace.tsx b/src/features/workspace/components/Workspace.tsx index 3377fc9..dbe17b6 100644 --- a/src/features/workspace/components/Workspace.tsx +++ b/src/features/workspace/components/Workspace.tsx @@ -1,6 +1,7 @@ import { Box, Text } from "@chakra-ui/react"; import { DataSelector } from "features/selectData/components/DataSelector"; import { SQLEditor } from "features/sqlEditor/components/SQLEditor"; +import { TableGrid } from "features/sqlEditor/components/TableGrid"; import { IJsonModel, Layout, @@ -22,6 +23,7 @@ const ComponentsMap: Record> = { fields: Fields, sqlEditor: SQLEditor, selectData: DataSelector, + tableGrid: TableGrid }; export const Workspace = () => { diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..4a355ad --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,6 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' +import type { RootState, Dispatch } from 'store/store' + +// Use throughout your app instead of plain `useDispatch` and `useSelector` +export const useAppDispatch = () => useDispatch() +export const useAppSelector: TypedUseSelectorHook = useSelector \ No newline at end of file diff --git a/src/models/workspace.ts b/src/models/workspace.ts index 9342af8..378e588 100644 --- a/src/models/workspace.ts +++ b/src/models/workspace.ts @@ -42,7 +42,7 @@ const initialModel = { type: "tab", enableClose: false, name: "Table View", - component: "button", + component: "tableGrid", }, ], }, diff --git a/src/utils/arrow-utils.ts b/src/utils/arrow-utils.ts new file mode 100644 index 0000000..0db3fc6 --- /dev/null +++ b/src/utils/arrow-utils.ts @@ -0,0 +1,22 @@ +// from import { valueToString } from "apache-arrow/util/pretty"; +export function valueToString(x) { + if (x === null) { + return 'null'; + } + if (x === undefined) { + return 'undefined'; + } + switch (typeof x) { + case 'number': return `${x}`; + case 'bigint': return `${x}`; + case 'string': return `"${x}"`; + } + // If [Symbol.toPrimitive] is implemented (like in BN) + // use it instead of JSON.stringify(). This ensures we + // print BigInts, Decimals, and Binary in their native + // representation + if (typeof x[Symbol.toPrimitive] === 'function') { + return x[Symbol.toPrimitive]('string'); + } + return ArrayBuffer.isView(x) ? `[${x}]` : JSON.stringify(x); +} \ No newline at end of file