mirror of
https://github.com/asadbek064/hyparquet.git
synced 2025-12-05 22:41:55 +00:00
HighTable demo
This commit is contained in:
parent
c20cc90af9
commit
1de02e9747
11
demo/bundle.min.js
vendored
Normal file
11
demo/bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
demo/bundle.min.js.map
Normal file
1
demo/bundle.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -99,6 +99,24 @@ input[type="file"] {
|
||||
}
|
||||
|
||||
/* table */
|
||||
.table-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
.table-scroll {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
.table-scroll > div {
|
||||
position: relative;
|
||||
}
|
||||
.table-scroll .table {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
@ -132,6 +150,22 @@ th, td {
|
||||
overflow: hidden;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* column resize */
|
||||
.table thead span {
|
||||
position: absolute;
|
||||
border-right: 1px solid #ddd;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 8px;
|
||||
cursor: col-resize;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.table thead span:hover {
|
||||
background-color: #aab;
|
||||
}
|
||||
|
||||
/* row numbers */
|
||||
td:first-child {
|
||||
background-color: #eaeaeb;
|
||||
@ -148,6 +182,30 @@ td:first-child {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
/* table corner */
|
||||
.table-corner {
|
||||
background-color: #e4e4e6;
|
||||
border-right: 1px solid #ccc;
|
||||
position: absolute;
|
||||
height: 44px;
|
||||
width: 32px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 15;
|
||||
box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* mock row numbers */
|
||||
.mock-row-label {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: #eaeaeb;
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
#filename {
|
||||
font-size: 10pt;
|
||||
margin-top: 20px;
|
||||
|
||||
91
demo/demo.js
91
demo/demo.js
@ -1,8 +1,11 @@
|
||||
import HighTable from 'hightable'
|
||||
import { compressors } from 'hyparquet-compressors'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import {
|
||||
parquetMetadata, parquetMetadataAsync, parquetRead, parquetSchema, toJson,
|
||||
} from '../src/hyparquet.js'
|
||||
import { asyncBufferFromUrl } from '../src/utils.js'
|
||||
import { compressors } from './hyparquet-compressors.min.js'
|
||||
import { fileLayout, fileMetadata } from './layout.js'
|
||||
|
||||
/**
|
||||
@ -97,27 +100,32 @@ function processFile(file) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AsyncBuffer} asyncBuffer
|
||||
* @param {AsyncBuffer} file
|
||||
* @param {FileMetaData} metadata
|
||||
* @param {string} name
|
||||
*/
|
||||
async function render(asyncBuffer, metadata, name) {
|
||||
renderSidebar(asyncBuffer, metadata, name)
|
||||
function render(file, metadata, name) {
|
||||
renderSidebar(file, metadata, name)
|
||||
|
||||
const { children } = parquetSchema(metadata)
|
||||
const header = children.map(child => child.element.name)
|
||||
|
||||
const startTime = performance.now()
|
||||
await parquetRead({
|
||||
compressors,
|
||||
file: asyncBuffer,
|
||||
rowEnd: 1000,
|
||||
onComplete(/** @type {any[][]} */ data) {
|
||||
const ms = performance.now() - startTime
|
||||
console.log(`parsed ${name} in ${ms.toFixed(0)} ms`)
|
||||
content.appendChild(renderTable(header, data))
|
||||
const dataframe = {
|
||||
header: children.map(child => child.element.name),
|
||||
numRows: Number(metadata.num_rows),
|
||||
/**
|
||||
* @param {number} rowStart
|
||||
* @param {number} rowEnd
|
||||
* @returns {Promise<any[][]>}
|
||||
*/
|
||||
rows(rowStart, rowEnd) {
|
||||
console.log(`reading rows ${rowStart}-${rowEnd}`)
|
||||
return new Promise((resolve, reject) => {
|
||||
parquetRead({ file, compressors, rowStart, rowEnd, onComplete: resolve })
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
renderTable(dataframe)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,51 +151,12 @@ fileInput?.addEventListener('change', () => {
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {string[]} header
|
||||
* @param {any[][] | Record<string, any>[]} data
|
||||
* @returns {HTMLTableElement}
|
||||
* @param {import('hightable').DataFrame} data
|
||||
*/
|
||||
function renderTable(header, data) {
|
||||
const table = document.createElement('table')
|
||||
const thead = document.createElement('thead')
|
||||
const tbody = document.createElement('tbody')
|
||||
const headerRow = document.createElement('tr')
|
||||
headerRow.appendChild(document.createElement('th'))
|
||||
for (const columnName of header) {
|
||||
const th = document.createElement('th')
|
||||
th.innerText = columnName
|
||||
headerRow.appendChild(th)
|
||||
}
|
||||
thead.appendChild(headerRow)
|
||||
table.appendChild(thead)
|
||||
for (const row of data) {
|
||||
const tr = document.createElement('tr')
|
||||
const rowNumber = document.createElement('td')
|
||||
rowNumber.innerText = String(tbody.children.length + 1)
|
||||
tr.appendChild(rowNumber)
|
||||
for (const value of Object.values(row)) {
|
||||
const td = document.createElement('td')
|
||||
td.innerText = stringify(value)
|
||||
tr.appendChild(td)
|
||||
}
|
||||
tbody.appendChild(tr)
|
||||
}
|
||||
table.appendChild(tbody)
|
||||
return table
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} value
|
||||
* @param {number} depth
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringify(value, depth = 0) {
|
||||
if (value === null) return depth ? 'null' : ''
|
||||
if (value === undefined) return depth ? 'undefined' : ''
|
||||
if (typeof value === 'bigint') return value.toString()
|
||||
if (typeof value === 'string') return value
|
||||
if (Array.isArray(value)) return `[${value.map(v => stringify(v, depth + 1)).join(', ')}]`
|
||||
if (value instanceof Date) return value.toISOString()
|
||||
if (typeof value === 'object') return `{${Object.entries(value).map(([k, v]) => `${k}: ${stringify(v, depth + 1)}`).join(', ')}}`
|
||||
return value
|
||||
function renderTable(data) {
|
||||
// Load HighTable.tsx and render
|
||||
const container = document.getElementById('content')
|
||||
// @ts-expect-error ReactDOM type issue
|
||||
const root = ReactDOM.createRoot(container)
|
||||
root.render(React.createElement(HighTable, { data }))
|
||||
}
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>hyparquet parquet file parser</title>
|
||||
<title>hyparquet parquet file parser demo</title>
|
||||
<link rel="icon" href="favicon.png" />
|
||||
<link rel="stylesheet" href="demo/demo.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;600&display=swap"/>
|
||||
<meta name="description" content="Online demo of hyparquet: a parser for apache parquet files. Drag and drop parquet files to view parquet data.">
|
||||
<meta name="author" content="Hyperparam">
|
||||
<meta name="keywords" content="hyparquet, parquet, parquet file, parquet parser, parquet reader, parquet viewer, parquet data, apache parquet">
|
||||
<meta name="keywords" content="hyparquet, parquet, parquet file, parquet parser, parquet reader, parquet viewer, parquet data, apache parquet, hightable">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
@ -21,6 +21,7 @@
|
||||
<h2>parquet file reader</h2>
|
||||
<p>
|
||||
Online demo of <a href="https://github.com/hyparam/hyparquet">hyparquet</a>: a parser for apache parquet files.
|
||||
Uses <a href="https://github.com/hyparam/hightable">hightable</a> for high performance windowed table viewing.
|
||||
</p>
|
||||
<p>
|
||||
Drag and drop a parquet file onto the dropzone to see parquet data.
|
||||
@ -39,6 +40,6 @@
|
||||
</div>
|
||||
<input id="file-input" type="file">
|
||||
|
||||
<script type="module" src="demo/demo.js"></script>
|
||||
<script type="module" src="demo/bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
15
package.json
15
package.json
@ -22,18 +22,29 @@
|
||||
"scripts": {
|
||||
"coverage": "vitest run --coverage --coverage.include=src",
|
||||
"demo": "http-server -o",
|
||||
"demo:build": "rollup -c",
|
||||
"lint": "eslint .",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "22.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.2.0",
|
||||
"@rollup/plugin-commonjs": "26.0.1",
|
||||
"@rollup/plugin-node-resolve": "15.2.3",
|
||||
"@rollup/plugin-replace": "5.0.7",
|
||||
"@rollup/plugin-terser": "0.4.4",
|
||||
"@types/node": "22.5.1",
|
||||
"@types/react": "18.3.4",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.3.0",
|
||||
"@vitest/coverage-v8": "2.0.5",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-jsdoc": "50.2.2",
|
||||
"hightable": "0.2.3",
|
||||
"http-server": "14.1.1",
|
||||
"hyparquet-compressors": "0.1.4",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"rollup": "4.21.1",
|
||||
"typescript": "5.5.4",
|
||||
"vitest": "2.0.5"
|
||||
}
|
||||
|
||||
22
rollup.config.js
Normal file
22
rollup.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import replace from '@rollup/plugin-replace'
|
||||
import terser from '@rollup/plugin-terser'
|
||||
|
||||
export default {
|
||||
input: 'demo/demo.js',
|
||||
output: {
|
||||
file: 'demo/bundle.min.js',
|
||||
format: 'umd',
|
||||
sourcemap: true,
|
||||
},
|
||||
plugins: [
|
||||
commonjs(),
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify('production'), // or 'development' based on your build environment
|
||||
preventAssignment: true,
|
||||
}),
|
||||
resolve({ browser: true }),
|
||||
terser(),
|
||||
],
|
||||
}
|
||||
@ -6,9 +6,7 @@
|
||||
"module": "nodenext",
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": false,
|
||||
"strict": true,
|
||||
"target": "esnext",
|
||||
"strict": true
|
||||
},
|
||||
"include": ["src", "test", "demo"]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user