Skip to content

Instantly share code, notes, and snippets.

@skysan87
Last active January 3, 2024 07:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skysan87/c7dc124e30494c5e5dfc8e79f31b1632 to your computer and use it in GitHub Desktop.
Save skysan87/c7dc124e30494c5e5dfc8e79f31b1632 to your computer and use it in GitHub Desktop.
[Vue 3][TailwindCSS 3] CSV Reader on Browser
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSV Viewer</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/encoding-japanese/2.0.0/encoding.min.js"></script>
<script type="importmap">
{
"imports": {
"Vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
}
}
</script>
</head>
<body>
<div id="app">
<div class="app-container">
<!-- header -->
<div class="grid-header p-2">
<button class="border bg-white active:bg-blue-200 p-1" @click="openFile">ファイルを開く</button>
</div>
<!-- table -->
<div class="grid-table-view overflow-auto">
<div class="grid-table">
<div class="row header sticky top-0 z-20">
<div class="cell bg-gray-200 sticky left-0"></div>
<div class="cell bg-gray-200" v-for="(cell, j) in header" :key="`cell-${j}`">{{ cell }}</div>
</div>
<div class="row" v-for="(row, i) in rows" :key="`cell-${i}`" @click="rowSelected(i)">
<div class="cell bg-white sticky left-0 z-10">{{ i + 1 }}</div>
<div class="cell bg-white" v-for="(cell, j) in row" :key="`cell-${j}`">{{ cell }}</div>
</div>
</div>
</div>
<!-- row data -->
<div class="grid-record-view overflow-hidden flex flex-col">
<div class="flex-none p-2">
<button class="border bg-white p-1" @click="selectedIndex = -1">Clear</button>
<span class="pl-2" v-if="selectedIndex > -1">{{ selectedIndex + 1}} 行目</span>
</div>
<div class="flex-1 overflow-auto">
<div class="grid-table fixed-table pointer-events-none w-full">
<div class="row" v-for="(cell, j) in rows[selectedIndex]" :key="`cell-${j}`">
<div class="cell bg-gray-200">{{ header[j] }}</div>
<div class="cell bg-white">{{ cell }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
async function openFilePicker () {
const pickerOpts = {
types: [
{
accept: {
'text/*': ['.csv', '.tsv']
}
},
],
excludeAcceptAllOption: true,
multiple: false
}
const [fileHandle] = await showOpenFilePicker(pickerOpts)
const file = await fileHandle.getFile()
const extension = file.name.split('.').pop()
const parseType = {
'csv': ',',
'tsv': '\t'
}
return {
text: toText(await file.arrayBuffer()),
splitter: parseType[extension]
}
}
function toText (arrayBuffer) {
const unit8Array = new Uint8Array(arrayBuffer)
const detectedEncoding = Encoding.detect(unit8Array)
console.log('File Encoding: ', detectedEncoding)
if (detectedEncoding === 'UTF8') {
Encoding.codeToString(unit8Array)
}
const unicodeArray = Encoding.convert(unit8Array, { from: detectedEncoding, to: 'UNICODE' })
return Encoding.codeToString(unicodeArray)
}
</script>
<script type="module">
import { createApp, ref } from 'Vue'
createApp({
setup () {
const header = ref([])
const rows = ref([])
const selectedIndex = ref(-1)
const openFile = async () => {
const { text, splitter } = await openFilePicker()
parse(text, splitter)
}
const parse = (text, splitter) => {
header.value.length = 0
rows.value.length = 0
text.split('\n')
.forEach(row => {
if (row !== '') {
rows.value.push(row.split(splitter))
}
})
header.value = rows.value.shift()
}
const rowSelected = (rowIndex) => {
selectedIndex.value = rowIndex
}
return {
header,
rows,
selectedIndex,
openFile,
rowSelected
}
}
}).mount('#app')
</script>
<style>
.app-container {
display: grid;
height: 100vh;
width: 100vw;
grid-template-rows: 50px auto;
grid-template-columns: 70% 30%;
grid-template-areas: "header header"
"table-view record-view";
}
.grid-header {
grid-area: header;
background-color: #FF69A3;
}
.grid-table-view {
grid-area: table-view;
background-color: #75A9FF;
}
.grid-record-view {
grid-area: record-view;
background-color: #D0FF43;
}
.fixed-table {
table-layout: fixed;
}
.grid-table {
display: table;
border-left: 1px solid black;
font-size: 1rem;
}
.row {
display: table-row;
}
.row:hover>.cell {
background-color: red !important;
}
.grid-record-view .grid-table>.row:first-child>.cell {
border-top: 1px solid black;
}
.header {
pointer-events: none;
}
.cell {
display: table-cell;
border-right: 1px solid black;
padding: 2px;
border-bottom: 1px solid black;
}
.header>.cell {
border-top: 1px solid black;
}
</style>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment