Frontend created and rewritten a few times, with some backend fixes #1
2
cista-front/components.d.ts
vendored
2
cista-front/components.d.ts
vendored
|
@ -18,11 +18,9 @@ declare module 'vue' {
|
|||
AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
|
||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||
APageHeader: typeof import('ant-design-vue/es')['PageHeader']
|
||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||
AppNavigation: typeof import('./src/components/AppNavigation.vue')['default']
|
||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
ARow: typeof import('ant-design-vue/es')['Row']
|
||||
ATable: typeof import('ant-design-vue/es')['Table']
|
||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||
FileCarousel: typeof import('./src/components/FileCarousel.vue')['default']
|
||||
FileExplorer: typeof import('./src/components/FileExplorer.vue')['default']
|
||||
|
|
|
@ -6,72 +6,26 @@
|
|||
<FileCarousel></FileCarousel>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
v-else-if="!documentStore.loading && documentStore.mainDocument"
|
||||
:pagination=false
|
||||
:row-selection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
|
||||
:columns="columns"
|
||||
:data-source="documentStore.mainDocument"
|
||||
>
|
||||
<template #headerCell="{column}"></template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<div class="editable-cell" :class="record.type === 'folder' ? 'folder' : 'file'">
|
||||
<div v-if="editableData[record.key]" class="action-container editable-cell-input-wrapper">
|
||||
<a-input class="name" v-model:value="editableData[record.key].name" @pressEnter="save(record.key)" />
|
||||
<CheckOutlined class="edit-action editable-cell-icon-check" @click="save(record.key)" />
|
||||
</div>
|
||||
<div v-else class="action-container editable-cell-text-wrapper">
|
||||
<a v-if="record.type === 'folder'" class="name" :href="`#${linkBasePath}/${record.name}`">{{record.name}}</a>
|
||||
<a v-else class="name" :href="`${filesBasePath}/${record.name}`">{{record.name}}</a>
|
||||
<edit-outlined class="edit-action editable-cell-icon" @click="edit(record.key)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-popover trigger="click">
|
||||
<template #content>
|
||||
<div class="more-action">
|
||||
<div class="action-container">
|
||||
<a :href="`${record.type === 'folder'? linkBasePath+'#' : filesBasePath}/${record.name}`">
|
||||
<a-button type="text" class="action-button" :icon="h(ImportOutlined)"/>
|
||||
</a>
|
||||
Open
|
||||
</div>
|
||||
<div v-if="record.type === 'folder-file'" class="action-container">
|
||||
<a :href="`${filesBasePath}/${record.name}`" download>
|
||||
<a-button type="text" class="action-button" :icon="h(DownloadOutlined)"/>
|
||||
</a>
|
||||
Download
|
||||
</div>
|
||||
<div class="action-container">
|
||||
<a-button type="text" class="action-button" @click="edit(record.key)" :icon="h(EditOutlined)"/> Rename
|
||||
</div>
|
||||
<div class="action-container">
|
||||
<a-button
|
||||
type="text"
|
||||
class="action-button"
|
||||
@click="share(`${record.type === 'folder'? linkBasePath+'/#' : filesBasePath}/${record.name}`)"
|
||||
:icon="h(LinkOutlined)"
|
||||
/> Share
|
||||
</div>
|
||||
<div class="action-container">
|
||||
<a-button type="text" class="action-button" :icon="h(CopyOutlined)"/> Copy
|
||||
</div>
|
||||
<div class="action-container">
|
||||
<a-button type="text" class="action-button" :icon="h(ScissorOutlined)"/> Cut
|
||||
</div>
|
||||
<div class="action-container">
|
||||
<a-button type="text" class="action-button" :icon="h(DeleteOutlined)"/> Delete
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-button type="text" class="action-button" :icon="h(EllipsisOutlined)" />
|
||||
</a-popover>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</a-table>
|
||||
<table v-else-if="!documentStore.loading && documentStore.mainDocument">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="selection"><input type="checkbox" v-model="allSelected" :indeterminate="selectionIndeterminate"></th>
|
||||
<th class="sortcolumn" :class="{sortactive: sort === 'name'}" @click="toggleSort('name')">Name</th>
|
||||
<th class="sortcolumn modified right" :class="{sortactive: sort === 'modified'}" @click="toggleSort('modified')">Modified</th>
|
||||
<th class="sortcolumn size right" :class="{sortactive: sort === 'size'}" @click="toggleSort('size')">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="doc of sorted(documentStore.mainDocument as FolderDocument[])" :key="doc.key" :class="doc.type === 'folder' ? 'folder' : 'file'">
|
||||
<td class="selection"><input type="checkbox" v-model="doc.selected"></td>
|
||||
<td class="name">
|
||||
<a :href="url_for(doc)">{{doc.name}}</a>
|
||||
</td>
|
||||
<td class="right">{{doc.modified}}</td>
|
||||
<td class="right">{{doc.sizedisp}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</main>
|
||||
</template>
|
||||
|
@ -79,10 +33,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, h, computed, reactive, watchEffect } from 'vue'
|
||||
import type { UnwrapRef } from 'vue'
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { useDocumentStore } from '@/stores/documents'
|
||||
import { EditOutlined, ImportOutlined, CheckOutlined,CopyOutlined, ScissorOutlined, LinkOutlined, DownloadOutlined, DeleteOutlined, EllipsisOutlined } from '@ant-design/icons-vue'
|
||||
import type { TableColumnsType } from 'ant-design-vue';
|
||||
import Router from '@/router/index';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { Document, FolderDocument } from '@/repositories/Document';
|
||||
|
@ -104,70 +55,95 @@
|
|||
return path === '/' ? '' : path
|
||||
})
|
||||
const filesBasePath = computed(() => `/files${linkBasePath.value}`)
|
||||
const url_for = (doc: FolderDocument) => doc.type === "folder" ? `#${linkBasePath.value}/${doc.name}` : `${filesBasePath}/${doc.name}`
|
||||
|
||||
const columns = ref<TableColumnsType>([
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
width: '70%',
|
||||
key: 'name',
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
sorter: (a: Document, b: Document) => a.name.localeCompare(b.name),
|
||||
const toggleSort = (name: string) => { sort.value = sort.value === name ? "" : name }
|
||||
const sort = ref<string>("")
|
||||
const sortCompare = {
|
||||
"name": (a: Document, b: Document) => a.name.localeCompare(b.name),
|
||||
"modified": (a: FolderDocument, b: FolderDocument) => b.mtime - a.mtime,
|
||||
"size": (a: FolderDocument, b: FolderDocument) => b.size - a.size
|
||||
}
|
||||
const sorted = (documents: FolderDocument[]) => {
|
||||
const cmp = sortCompare[sort.value as keyof typeof sortCompare]
|
||||
const sorted = [...documents]
|
||||
if (cmp) sorted.sort(cmp)
|
||||
return sorted
|
||||
}
|
||||
const selectionIndeterminate = computed({
|
||||
get: () => {
|
||||
return documentStore.mainDocument && documentStore.mainDocument.length > 0 && documentStore.mainDocument.some((doc: Document) => doc.selected) && !allSelected.value
|
||||
},
|
||||
{
|
||||
title: 'Modified',
|
||||
dataIndex: 'modified',
|
||||
className: 'column-date',
|
||||
responsive: ['lg'],
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
defaultSortOrder: 'descend',
|
||||
sorter: (a: FolderDocument, b: FolderDocument) => a.mtime - b.mtime,
|
||||
key: 'modified',
|
||||
set: (value: boolean) => {}
|
||||
})
|
||||
const allSelected = computed({
|
||||
get: () => {
|
||||
return documentStore.mainDocument && documentStore.mainDocument.length > 0 && documentStore.mainDocument.every((doc: Document) => doc.selected)
|
||||
},
|
||||
{
|
||||
// TODO BETTER SORT FOR MULTPLE SIZE OR CUSTOM PIPE TO kB to MB / GB
|
||||
title: 'Size',
|
||||
dataIndex: 'sizedisp',
|
||||
className: 'column-size',
|
||||
responsive: ['lg'],
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
sorter: (a: FolderDocument, b: FolderDocument) => a.size - b.size,
|
||||
key: 'size',
|
||||
},
|
||||
{
|
||||
width: '5%',
|
||||
key: 'action',
|
||||
},
|
||||
])
|
||||
|
||||
const onSelectChange = (selectedRowKeys: Key[]) => {
|
||||
const newSelectedRowKeys: Document[] = []
|
||||
selectedRowKeys.forEach( key => {
|
||||
if(documentStore.mainDocument){
|
||||
const found = documentStore.mainDocument.find( e=> e.key === key )
|
||||
if(found) newSelectedRowKeys.push(found)
|
||||
set: (value: boolean) => {
|
||||
if (documentStore.mainDocument) {
|
||||
documentStore.mainDocument.forEach((doc: Document) => doc.selected = value)
|
||||
}
|
||||
}
|
||||
})
|
||||
documentStore.setSelectedDocuments(newSelectedRowKeys)
|
||||
state.selectedRowKeys = selectedRowKeys;
|
||||
};
|
||||
const edit = (key: number) => {
|
||||
editableData[key] = cloneDeep(documentStore.mainDocument.filter(item => key === item.key)[0]);
|
||||
};
|
||||
const save = (key: number) => {
|
||||
Object.assign(documentStore.mainDocument.filter(item => key === item.key)[0], editableData[key]);
|
||||
delete editableData[key];
|
||||
};
|
||||
const share = async (url : string) => {
|
||||
await navigator.clipboard.writeText(location.origin + url)
|
||||
messageApi.success("Link successfully copied to the clipboard");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.column-date, .column-size {
|
||||
table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
table input[type=checkbox] {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
table .modified { width: 10em; }
|
||||
table .size { width: 6em; }
|
||||
table th, table td {
|
||||
padding: .5em;
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
thead tr {
|
||||
border: 1px solid #ddd;
|
||||
background: #ddd;
|
||||
}
|
||||
tbody tr {
|
||||
background: #444;
|
||||
color: #ddd;
|
||||
}
|
||||
tbody tr:hover {
|
||||
background: #00f8;
|
||||
}
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
.selection {
|
||||
width: 2em;
|
||||
}
|
||||
.sortcolumn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sortcolumn:hover::after {
|
||||
color: #f80;
|
||||
}
|
||||
.sortcolumn {
|
||||
padding-right: 1.7em;
|
||||
}
|
||||
.sortcolumn::after {
|
||||
content: "▸";
|
||||
color: #888;
|
||||
margin: 0 1em 0 .5em;
|
||||
position: absolute;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
.sortactive::after {
|
||||
transform: rotate(90deg);
|
||||
color: #000;
|
||||
}
|
||||
main {
|
||||
padding: 5px;
|
||||
height: 100%;
|
||||
|
@ -190,6 +166,9 @@
|
|||
.carousel-container{
|
||||
height: inherit;
|
||||
}
|
||||
.name a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.file .name::before {
|
||||
content: '📄 ';
|
||||
font-size: 1.5em;
|
||||
|
|
|
@ -5,8 +5,9 @@ import Client from '@/repositories/Client'
|
|||
|
||||
|
||||
type BaseDocument = {
|
||||
name: string;
|
||||
key?: number | string;
|
||||
name: string
|
||||
key?: number | string
|
||||
selected?: boolean
|
||||
};
|
||||
|
||||
export type FolderDocument = BaseDocument & {
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Vite Vasanko</title>
|
||||
<script type="module" crossorigin src="/assets/index-1ae30b84.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-f91a1fb3.css">
|
||||
<script type="module" crossorigin src="/assets/index-06f39339.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-38d160e9.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
Loading…
Reference in New Issue
Block a user