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] ?? "&nbsp;"}</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()]
+})