mirror of
https://github.com/xlmnxp/extractify.zip.git
synced 2024-11-23 17:13:12 +03:00
add drag select element
This commit is contained in:
commit
5d1fd59294
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
node_modules
|
||||
*.log*
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
.output
|
||||
.env
|
||||
dist
|
||||
.DS_Store
|
42
README.md
Normal file
42
README.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Nuxt 3 Minimal Starter
|
||||
|
||||
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install the dependencies:
|
||||
|
||||
```bash
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
175
app.vue
Normal file
175
app.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<script setup lang="ts">
|
||||
import { Archive } from 'libarchive.js/main.js';
|
||||
import { CompressedFile } from 'libarchive.js/src/compressed-file';
|
||||
import { getElementInfo } from "moveable";
|
||||
import { VueSelecto } from "vue3-selecto";
|
||||
|
||||
Archive.init({
|
||||
workerUrl: '/worker-bundle.js',
|
||||
});
|
||||
|
||||
let drawer = ref(true);
|
||||
let files = ref([]);
|
||||
|
||||
let filesList = ref<any>([]);
|
||||
let selectedItem = useSelectedItem();
|
||||
|
||||
watchEffect(async () => {
|
||||
if (files.value?.[0]) {
|
||||
filesList.value = [];
|
||||
|
||||
const archive = await Archive.open(files.value[0]);
|
||||
|
||||
// console.log(await archive.getFilesObject())
|
||||
let extractedFiles = await archive.getFilesObject();
|
||||
|
||||
let getContent = (fileList: any, path = ''): any => {
|
||||
return Object.keys(fileList).map(file => ({
|
||||
name: file,
|
||||
path: `${path}/${file}${!(fileList[file] instanceof File || fileList[file] instanceof CompressedFile) ? '/' : ''}`,
|
||||
isFolder: !(fileList[file] instanceof File || fileList[file] instanceof CompressedFile),
|
||||
content: !(fileList[file] instanceof File || fileList[file] instanceof CompressedFile) && fileList[file] ? getContent(fileList[file], `${path}/${file}`)?.sort((a: any, b: any) => {
|
||||
return b.isFolder - a.isFolder
|
||||
}) : fileList[file]
|
||||
}))
|
||||
}
|
||||
|
||||
filesList.value = getContent(extractedFiles)?.sort((a: any, b: any) => {
|
||||
return b.isFolder - a.isFolder
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
function onDrop(e: any) {
|
||||
files.value = e.dataTransfer.files;
|
||||
selectedItem.value = '/';
|
||||
}
|
||||
|
||||
function preventDefaults(e: any) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const events = ['dragenter', 'dragover', 'dragleave', 'drop']
|
||||
|
||||
onMounted(() => {
|
||||
events.forEach((eventName) => {
|
||||
document.body.addEventListener(eventName, preventDefaults)
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
events.forEach((eventName) => {
|
||||
document.body.removeEventListener(eventName, preventDefaults)
|
||||
})
|
||||
})
|
||||
|
||||
function getFile(path: string, innerList = undefined): any {
|
||||
if (path == "/") {
|
||||
return {
|
||||
content: filesList.value
|
||||
};
|
||||
}
|
||||
|
||||
for (const file of (innerList || filesList.value)) {
|
||||
if (file.path == path) {
|
||||
return file;
|
||||
}
|
||||
|
||||
if (file.isFolder && path.includes(file.path)) {
|
||||
let recursiveFile = getFile(path, file.content);
|
||||
if (recursiveFile) {
|
||||
return recursiveFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let filesGridList = ref<any>([])
|
||||
|
||||
watchEffect(() => {
|
||||
filesGridList.value = getFile(selectedItem.value)?.content || [];
|
||||
})
|
||||
|
||||
let dragContainer = document.querySelector(".select-area");
|
||||
|
||||
function onSelectStart(e: any) {
|
||||
console.log("start", e);
|
||||
e.added.forEach((el: any) => {
|
||||
el.classList.add("selected");
|
||||
});
|
||||
e.removed.forEach((el: any) => {
|
||||
el.classList.remove("selected");
|
||||
});
|
||||
}
|
||||
|
||||
function onSelectEnd(e: any) {
|
||||
console.log("end", e);
|
||||
e.afterAdded.forEach((el: any) => {
|
||||
el.classList.add("selected");
|
||||
});
|
||||
e.afterRemoved.forEach((el: any) => {
|
||||
el.classList.remove("selected");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<v-layout @drop.prevent="onDrop">
|
||||
<v-app-bar color="light-blue-darken-1">
|
||||
<v-btn variant="text" icon="mdi-menu" v-slot:prepend @click="drawer = !drawer"></v-btn>
|
||||
<v-toolbar-title>Uncompressed File</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
</v-app-bar>
|
||||
|
||||
<v-navigation-drawer v-model="drawer" floating permanent>
|
||||
<v-toolbar density="comfortable" title="Files">
|
||||
<template v-slot:prepend>
|
||||
<v-btn icon="mdi-home" @click="selectedItem = '/'"></v-btn>
|
||||
</template>
|
||||
</v-toolbar>
|
||||
<TreeView :filesList="filesList" :nav=true></TreeView>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-main class="select-area" style="height: 100vh;">
|
||||
<v-toolbar class="px-5" density="comfortable">
|
||||
<v-text-field hide-details single-line placeholder="location" v-model="selectedItem"></v-text-field>
|
||||
</v-toolbar>
|
||||
<template v-if="selectedItem.endsWith('/')">
|
||||
<v-container>
|
||||
<v-list :selected="[selectedItem]">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="6" sm="2" class="" v-for="file of filesGridList" style="text-align: center;">
|
||||
<v-list-item class="ma-2 pa-5 selectable" active-color="light-blue-darken-4" :value="file.path" rounded="xl" @click="() => {
|
||||
selectedItem = file.path;
|
||||
}">
|
||||
<v-avatar class="mb-2" :color="file.isFolder ? 'light-blue-accent-4' : 'blue-grey-darken-1'">
|
||||
<v-icon color="white">{{ file.isFolder ? 'mdi-folder' : 'mdi-file' }}</v-icon>
|
||||
</v-avatar>
|
||||
<p>{{ file.name }}</p>
|
||||
</v-list-item>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-list>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<VueSelecto :selectableTargets="['.selectable']" :dragContainer="dragContainer" :hitRate="20"
|
||||
:selectFromInside="false" :toggleContinueSelect="'shift'" @select="onSelectStart" @selectStart="onSelectStart"
|
||||
:get-element-rect="getElementInfo"
|
||||
@selectEnd="onSelectEnd" />
|
||||
<h1 v-if="!files.length">Drag and drop compressed files here</h1>
|
||||
</v-main>
|
||||
</v-layout>
|
||||
</template>
|
||||
<style>
|
||||
.v-list-item {
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.selected {
|
||||
border: 2px solid rgba(48,150,243, 0.95);
|
||||
background: rgba(48,150,243, 0.1);
|
||||
}
|
||||
</style>
|
37
components/tree-view.vue
Normal file
37
components/tree-view.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { CompressedFile } from 'libarchive.js/src/compressed-file';
|
||||
|
||||
interface Props {
|
||||
filesList: (File | CompressedFile) & { name: string, path: string, toggle: Boolean, isFolder: Boolean, content: any, active: boolean } [],
|
||||
nav: boolean
|
||||
}
|
||||
|
||||
let {filesList, nav} = defineProps<Props>()
|
||||
let selectedItem = useSelectedItem();
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<v-list :selected="[selectedItem]" density="compact" :nav="nav">
|
||||
<template v-for="file in filesList" :key="file.path">
|
||||
<v-list-item active-color="light-blue-darken-4" :active=file.active :title="file.name" :subtitle="file.path" :value="file.path"
|
||||
@click="() =>{
|
||||
selectedItem = file.path;
|
||||
file.toggle = !file.toggle;
|
||||
}">
|
||||
<template v-slot:prepend>
|
||||
<v-avatar :color="file.isFolder ? 'light-blue-accent-4' : 'blue-grey-darken-1'">
|
||||
<v-icon color="white">{{ file.isFolder ? 'mdi-folder' : 'mdi-file' }}</v-icon>
|
||||
</v-avatar>
|
||||
</template>
|
||||
<template v-if="file.isFolder" v-slot:append>
|
||||
<v-icon>{{ `mdi-chevron-${file.toggle ? 'up' : 'down'}` }}</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<div :style="`background-color: rgba(0,0,0,0.05);${nav ? `border-radius: 4px;` : ''}` ">
|
||||
<template v-if="file.isFolder && file.toggle">
|
||||
<TreeView :files-list="file.content" :nav="false"></TreeView>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</v-list>
|
||||
</template>
|
2
composables/states.ts
Normal file
2
composables/states.ts
Normal file
@ -0,0 +1,2 @@
|
||||
let selectedItem = ref("/")
|
||||
export const useSelectedItem = () => useState("selected-item", () => selectedItem)
|
14
nuxt.config.ts
Normal file
14
nuxt.config.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
css: ['vuetify/lib/styles/main.sass', '@mdi/font/css/materialdesignicons.min.css',],
|
||||
ssr: false,
|
||||
build: {
|
||||
transpile: ['vuetify'],
|
||||
},
|
||||
vite: {
|
||||
define: {
|
||||
'process.env.DEBUG': false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
8173
package-lock.json
generated
Normal file
8173
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
package.json
Normal file
27
package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/libarchive.js": "^1.3.1",
|
||||
"@types/node": "^18",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"nuxt": "^3.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^7.2.96",
|
||||
"libarchive.js": "^1.3.0",
|
||||
"moveable": "^0.47.7",
|
||||
"sass": "^1.62.1",
|
||||
"selecto": "^1.22.3",
|
||||
"uuid": "^9.0.0",
|
||||
"vue3-selecto": "^1.8.4",
|
||||
"vuetify": "^3.2.4"
|
||||
}
|
||||
}
|
13
plugins/vuetify.ts
Normal file
13
plugins/vuetify.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { createVuetify } from 'vuetify'
|
||||
import * as components from 'vuetify/components'
|
||||
import * as directives from 'vuetify/directives'
|
||||
|
||||
export default defineNuxtPlugin(nuxtApp => {
|
||||
const vuetify = createVuetify({
|
||||
ssr: true,
|
||||
components,
|
||||
directives,
|
||||
})
|
||||
|
||||
nuxtApp.vueApp.use(vuetify)
|
||||
})
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
15
public/wasm-gen/libarchive.js
Normal file
15
public/wasm-gen/libarchive.js
Normal file
File diff suppressed because one or more lines are too long
BIN
public/wasm-gen/libarchive.wasm
Normal file
BIN
public/wasm-gen/libarchive.wasm
Normal file
Binary file not shown.
1
public/worker-bundle.js
Normal file
1
public/worker-bundle.js
Normal file
File diff suppressed because one or more lines are too long
4
tsconfig.json
Normal file
4
tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
Loading…
Reference in New Issue
Block a user