From 2cbc28d6ede807c77f74b45ad77f1677beaa0db7 Mon Sep 17 00:00:00 2001 From: 0xc0Der <59133155+0xc0Der@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:11:02 +0000 Subject: [PATCH] vue-modify demo [ci skip] --- demos/vue-modify/.gitignore | 24 +++ demos/vue-modify/README.md | 10 ++ demos/vue-modify/package.json | 21 +++ demos/vue-modify/src/App.vue | 241 ++++++++++++++++++++++++++++ demos/vue-modify/src/env.d.ts | 8 + demos/vue-modify/src/main.ts | 4 + demos/vue-modify/tsconfig.json | 16 ++ demos/vue-modify/tsconfig.node.json | 8 + demos/vue-modify/vite.config.ts | 7 + 9 files changed, 339 insertions(+) create mode 100644 demos/vue-modify/.gitignore create mode 100644 demos/vue-modify/README.md create mode 100644 demos/vue-modify/package.json create mode 100644 demos/vue-modify/src/App.vue create mode 100644 demos/vue-modify/src/env.d.ts create mode 100644 demos/vue-modify/src/main.ts create mode 100644 demos/vue-modify/tsconfig.json create mode 100644 demos/vue-modify/tsconfig.node.json create mode 100644 demos/vue-modify/vite.config.ts diff --git a/demos/vue-modify/.gitignore b/demos/vue-modify/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/demos/vue-modify/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/demos/vue-modify/README.md b/demos/vue-modify/README.md new file mode 100644 index 0000000..4d76306 --- /dev/null +++ b/demos/vue-modify/README.md @@ -0,0 +1,10 @@ +# vue-modify + +This demo shows import an export with `vue3-table-light` table component. + +In this directory, run + +```bash +npm i +npm run dev +``` diff --git a/demos/vue-modify/package.json b/demos/vue-modify/package.json new file mode 100644 index 0000000..7874a9d --- /dev/null +++ b/demos/vue-modify/package.json @@ -0,0 +1,21 @@ +{ + "name": "vue-modify", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite --host", + "build": "vue-tsc --noEmit && vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.2.25", + "vue3-table-lite": "^1.1.7-1", + "xlsx": "^0.18.3" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^2.2.0", + "typescript": "^4.5.4", + "vite": "^2.8.0", + "vue-tsc": "^0.29.8" + } +} diff --git a/demos/vue-modify/src/App.vue b/demos/vue-modify/src/App.vue new file mode 100644 index 0000000..2b1f5ec --- /dev/null +++ b/demos/vue-modify/src/App.vue @@ -0,0 +1,241 @@ +<script setup lang="ts"> +import { ref } from "vue"; +import { read, utils, writeFile, WorkBook } from "xlsx"; + +import VueTableLite from "vue3-table-lite/ts"; + +type DataSet = { + [index: string]: WorkBook; +}; + +type Row = any[]; + +type Column = { + field: string; + label: string; + display: (row: Row) => string; +}; + +const currFileName = ref<string>(""); +const currSheet = ref<string>(""); +const sheets = ref<string[]>([]); +const workBook = ref<DataSet>({} as DataSet); +const rows = ref<Row[]>([]); +const columns = ref<Column[]>([]); + +const exportTypes: string[] = ["xlsx", "xlsb", "csv", "html"]; + +let cell = 0; + +function resetCell() { + cell = 0; +} + +function display(col: number): (row: Row) => string { + return function (row: Row) { + return `<span + style="user-select: none; display: block" + position="${Math.floor(cell++ / columns.value.length)}.${col}" + onblur="endEdit(event)" + ondblclick="startEdit(event)" + onkeydown="endEdit(event)">${row[col] ?? " "}</span>`; + }; +} + +window.startEdit = function (ev) { + ev.target.contentEditable = true; + ev.target.focus(); +}; + +window.endEdit = function (ev) { + if (ev.key === undefined || ev.key === "Enter") { + const pos = ev.target.getAttribute("position").split("."); + + ev.target.contentEditable = false; + + rows.value[pos[0]][pos[1]] = ev.target.innerText; + + workBook.value[currSheet.value] = utils.json_to_sheet(rows.value, { + header: columns.value.map((col: Column) => col.field), + skipHeader: true, + }); + } +}; + +function getRowsCols( + data: DataSet, + sheetName: string +): { + rows: Row[]; + cols: Column[]; +} { + const rows: Row[] = utils.sheet_to_json(data[sheetName], { header: 1 }); + let cols: Column[] = []; + + for (let row of rows) { + const keys: string[] = Object.keys(row); + + if (keys.length > cols.length) { + cols = keys.map((key) => { + return { + field: key, + label: utils.encode_col(+key), + display: display(key), + }; + }); + } + } + + return { rows, cols }; +} + +async function importFile(ev: ChangeEvent<HTMLInputElement>): Promise<void> { + const file = ev.target.files[0]; + const data = read(await file.arrayBuffer()); + + currFileName.value = file.name; + currSheet.value = data.SheetNames?.[0]; + sheets.value = data.SheetNames; + workBook.value = data.Sheets; + + selectSheet(currSheet.value); +} + +function exportFile(type: string): void { + const wb = utils.book_new(); + + sheets.value.forEach((sheet) => { + utils.book_append_sheet(wb, workBook.value[sheet], sheet); + }); + + writeFile(wb, `sheet.${type}`); +} + +function selectSheet(sheet: string): void { + const { rows: newRows, cols: newCols } = getRowsCols(workBook.value, sheet); + + resetCell(); + + rows.value = newRows; + columns.value = newCols; + currSheet.value = sheet; +} +</script> + +<template> + <header class="imp-exp"> + <div class="import"> + <input type="file" id="import" @change="importFile" /> + <label for="import">import</label> + </div> + <span>{{ currFileName || "vue-modify demo" }}</span> + <div class="export"> + <span>export</span> + <ul> + <li v-for="type in exportTypes" @click="exportFile(type)"> + {{ `.${type}` }} + </li> + </ul> + </div> + </header> + <div class="sheets"> + <span + v-for="sheet in sheets" + @click="selectSheet(sheet)" + :class="[currSheet === sheet ? 'selected' : '']" + > + {{ sheet }} + </span> + </div> + <vue-table-lite + :is-static-mode="true" + :page-size="50" + :columns="columns" + :rows="rows" + ></vue-table-lite> +</template> + +<style> +.imp-exp { + display: flex; + justify-content: space-between; + padding: 0.5rem; + font-family: mono; + color: #212529; +} + +.import { + font-size: medium; +} + +.import input { + position: absolute; + opacity: 0; + cursor: pointer; +} + +.import label { + background-color: white; + border: 1px solid; + padding: 0.3rem; +} + +.export: hover { + border-bottom: none; +} + +.export:hover ul { + display: block; +} + +.export span { + padding: 0.3rem; + border: 1px solid; + cursor: pointer; +} + +.export ul { + display: none; + position: absolute; + z-index: 5; + background-color: white; + list-style: none; + padding: 0.3rem; + border: 1px solid; + margin-top: 0.3rem; + border-top: none; +} + +.export ul li { + padding: 0.3rem; + text-align: center; +} + +.export ul li:hover { + background-color: lightgray; + cursor: pointer; +} + +.sheets { + display: flex; + justify-content: center; + margin: 0.3rem; + color: #212529; +} + +.sheets span { + border: 1px solid; + padding: 0.5rem; + margin: 0.3rem; +} + +.sheets span:hover:not(.selected) { + background-color: lightgray; + cursor: pointer; +} + +.selected { + background-color: #343a40; + color: white; +} +</style> diff --git a/demos/vue-modify/src/env.d.ts b/demos/vue-modify/src/env.d.ts new file mode 100644 index 0000000..aafef95 --- /dev/null +++ b/demos/vue-modify/src/env.d.ts @@ -0,0 +1,8 @@ +/// <reference types="vite/client" /> + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/demos/vue-modify/src/main.ts b/demos/vue-modify/src/main.ts new file mode 100644 index 0000000..684d042 --- /dev/null +++ b/demos/vue-modify/src/main.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue'; +import App from './App.vue'; + +createApp(App).mount('#app'); diff --git a/demos/vue-modify/tsconfig.json b/demos/vue-modify/tsconfig.json new file mode 100644 index 0000000..af31eb8 --- /dev/null +++ b/demos/vue-modify/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "esnext", + "useDefineForClassFields": true, + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"] + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/demos/vue-modify/tsconfig.node.json b/demos/vue-modify/tsconfig.node.json new file mode 100644 index 0000000..e993792 --- /dev/null +++ b/demos/vue-modify/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "composite": true, + "module": "esnext", + "moduleResolution": "node" + }, + "include": ["vite.config.ts"] +} diff --git a/demos/vue-modify/vite.config.ts b/demos/vue-modify/vite.config.ts new file mode 100644 index 0000000..315212d --- /dev/null +++ b/demos/vue-modify/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()] +})