Skip to content

Commit

Permalink
Cell view
Browse files Browse the repository at this point in the history
  • Loading branch information
platypii committed Jun 6, 2024
1 parent 5916823 commit 814c790
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 14 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Hyperparam
# hyperparam

[![npm](https://img.shields.io/npm/v/hyperparam)](https://www.npmjs.com/package/hyperparam)
[![workflow status](https://github.com/hyparam/hyperparam-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/hyparam/hyperparam-cli/actions)
[![mit license](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

This is the hyperparam cli tool.

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test": "vitest run"
},
"devDependencies": {
"@monaco-editor/react": "4.6.0",
"@rollup/plugin-commonjs": "26.0.1",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-replace": "5.0.7",
Expand All @@ -23,10 +24,10 @@
"@types/node": "20.14.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@typescript-eslint/eslint-plugin": "7.11.0",
"@typescript-eslint/eslint-plugin": "7.12.0",
"eslint": "8.57.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "48.2.7",
"eslint-plugin-jsdoc": "48.2.8",
"hightable": "0.2.1",
"hyparquet": "0.9.8",
"react": "18.3.1",
Expand Down
4 changes: 2 additions & 2 deletions public/bundle.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/bundle.min.js.map

Large diffs are not rendered by default.

74 changes: 74 additions & 0 deletions src/Cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import MonacoEditor from '@monaco-editor/react'
import { stringify } from 'hightable'
import React, { useEffect, useState } from 'react'
import Layout from './Layout.js'
import { parquetDataFrame } from './tableProvider.js'

enum LoadingState {
NotLoaded,
Loading,
Loaded
}

const editorOptions = {
colorDecorators: true,
contextmenu: false,
selectOnLineNumbers: true,
readOnly: true,
language: 'javascript',
automaticLayout: true,
}

/**
* Cell viewer displays a single cell from a table.
*/
export default function CellView(): JSX.Element {
const [loading, setLoading] = useState<LoadingState>(LoadingState.NotLoaded)
const [text, setText] = useState<string | undefined>()
const [error, setError] = useState<Error>()

// File path from url
const path = location.pathname.split('/')
const key = decodeURI(path.slice(2).join('/'))

// row, col from url
const search = new URLSearchParams(location.search)
const row = Number(search.get('row'))
const col = Number(search.get('col'))

// Load cell data
useEffect(() => {
async function loadCellData() {
try {
// TODO: handle first row > 100kb
const df = await parquetDataFrame(`/api/store/get?key=${key}`)
const rows = await df.rows(row, row + 1)
const cell = rows[0][col]
setText(stringify(cell))
} catch (error) {
setError(error as Error)
} finally {
setLoading(LoadingState.Loaded)
}
}

setLoading(loading => {
// use loading state to ensure we only load content once
if (loading !== LoadingState.NotLoaded) return loading
loadCellData()
return LoadingState.Loading
})
}, [col, row, loading, setError])

return <Layout error={error} title={key}>
{/* @ts-expect-error MonocoEditor type is wrong? */
<MonacoEditor
className='code'
height="100vh"
options={editorOptions}
theme='vs-dark'
value={text}
width="100%" />
}
</Layout>
}
18 changes: 11 additions & 7 deletions src/File.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import HighTable, { DataFrame } from 'hightable'
import React, { useEffect, useState } from 'react'
import type { FileContent } from './files.js'
import Layout, { Spinner } from './Layout.js'
import { parquetDataFrame } from './tableProvider.js'

Expand All @@ -13,31 +12,36 @@ export default function File() {

// File path from url
const path = location.pathname.split('/')
const prefix = decodeURI(path.slice(2).join('/'))
const key = decodeURI(path.slice(2).join('/'))

// Filename loaded immediately from url, file contents loaded async
const [loading, setLoading] = useState(false)
const [content, setContent] = useState<FileContent<void>>()

useEffect(() => {
parquetDataFrame('/api/store/get?key=' + prefix)
parquetDataFrame('/api/store/get?key=' + key)
.then(setDataframe)
.catch(setError)
.finally(() => setLoading(false))
}, [])

function onDoubleClickCell(row: number, col: number) {
location.href = '/files/' + key + '?row=' + row + '&col=' + col
}

return (
<Layout error={error} title={prefix}>
<Layout error={error} title={key}>
<nav className='top-header'>
<div className='path'>
<a href='/files'>/</a>
{prefix && prefix.split('/').map((sub, depth) =>
{key && key.split('/').map((sub, depth) =>
<a href={'/files/' + path.slice(2, depth + 3).join('/')} key={depth}>{sub}/</a>
)}
</div>
</nav>

{dataframe && <HighTable data={dataframe} />}
{dataframe &&
<HighTable data={dataframe} onDoubleClickCell={onDoubleClickCell} />
}

{loading && <Spinner className='center' />}
</Layout>
Expand Down
4 changes: 4 additions & 0 deletions src/render.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import ReactDOM from 'react-dom'
import Cell from './Cell.js'
import File from './File.js'
import Folder from './Folder.js'

Expand All @@ -12,6 +13,9 @@ function render() {
if (location.pathname.endsWith('/')) {
// Render folder view
root.render(React.createElement(Folder))
} else if (location.search) {
// Render cell view
root.render(React.createElement(Cell))
} else {
// Render file view
root.render(React.createElement(File))
Expand Down
1 change: 1 addition & 0 deletions src/tableProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { readableStreamToArrayBuffer } from './streamConverters.js'
* Construct a dataframe from a parquet file asynchronously.
*/
export async function parquetDataFrame(url: string): Promise<DataFrame> {
console.log('parquetDataFrame', url)
const asyncBuffer = await asyncBufferFrom(url)
// load parquet metadata
const metadata = await parquetMetadataAsync(asyncBuffer)
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"compilerOptions": {
"jsx": "react",
"allowJs": true,
"checkJs": true,
"jsx": "react",
"lib": ["esnext", "dom"],
"module": "nodenext",
"noEmit": true,
Expand Down

0 comments on commit 814c790

Please sign in to comment.