Skip to content

Commit

Permalink
Merge pull request #15 from HF6-PROJECT/kbi-9
Browse files Browse the repository at this point in the history
feat: Files & Folders
  • Loading branch information
frederikpyt authored Oct 5, 2023
2 parents a0cf399 + 0a2623d commit 4df7a9f
Show file tree
Hide file tree
Showing 18 changed files with 761 additions and 231 deletions.
373 changes: 161 additions & 212 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,26 @@
},
"dependencies": {
"@astrojs/check": "^0.2.0",
"@astrojs/node": "^6.0.0",
"@astrojs/node": "^6.0.2",
"@astrojs/sitemap": "^3.0.0",
"@astrojs/tailwind": "^5.0.0",
"@astrojs/ts-plugin": "^1.1.3",
"@astrojs/vercel": "^5.0.1",
"@astrojs/vue": "^3.0.0",
"@tauri-apps/api": "^1.4.0",
"@vercel/blob": "^0.12.5",
"astro": "^3.1.1",
"@tauri-apps/api": "^1.5.0",
"@vercel/blob": "^0.13.0",
"astro": "^3.2.2",
"flowbite": "^1.8.1",
"flowbite-typography": "^1.0.3",
"jose": "^4.14.6",
"jose": "^4.15.1",
"tailwind-scrollbar": "^3.0.5",
"tailwindcss": "^3.3.3",
"typescript": "^5.2.2",
"vite": "^4.4.9",
"vue": "^3.3.4"
},
"devDependencies": {
"@tauri-apps/cli": "^1.4.0",
"@tauri-apps/cli": "^1.5.1",
"dotenv": "^16.3.1",
"prettier": "^3.0.3",
"prettier-plugin-astro": "^0.12.0",
Expand Down
7 changes: 0 additions & 7 deletions src/components/Dashboard.astro

This file was deleted.

47 changes: 47 additions & 0 deletions src/components/files/ContextMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<div class="relative" v-on:contextmenu.prevent="openMenu">
<slot name="content"></slot>
<div
v-if="showMenu"
ref="menu"
v-on:blur="closeMenu"
tabindex="-1"
:style="{ top: `${top}px`, left: `${left}px` }"
class="absolute z-10 block w-44 divide-y divide-gray-100 rounded-lg bg-white shadow dark:divide-gray-600 dark:bg-gray-700"
>
<slot name="menu"></slot>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
const menu = ref<HTMLElement | null>(null);
const showMenu = ref(false);
const top = ref(0);
const left = ref(0);
onMounted(() => console.log(menu.value));
function setMenu(y: number, x: number) {
top.value = y;
left.value = x;
}
function closeMenu() {
showMenu.value = false;
}
function openMenu(e: MouseEvent) {
showMenu.value = true;
nextTick(() => {
if (!menu.value) return;
menu.value.focus();
setMenu(e.offsetY, e.offsetX);
});
}
</script>
23 changes: 23 additions & 0 deletions src/components/files/File.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<a
href="#"
class="block max-w-sm rounded-lg border border-gray-200 bg-white p-6 shadow hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700"
>
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
{{ modelValue.name }}
</h5>
</a>
</template>

<script setup lang="ts">
import { type PropType } from 'vue';
import { FileClass } from '@lib/items/files';
defineProps({
modelValue: {
type: Object as PropType<FileClass>,
required: true,
},
});
defineEmits(['update:modelValue']);
</script>
103 changes: 103 additions & 0 deletions src/components/files/FileBrowser.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<template>
<template v-if="!items.length">
<NoFiles />
</template>
<template v-else>
<div class="flex flex-wrap gap-3">
<!-- prettier-ignore-attribute -->
<Folder
v-for="folder in folders"
:key="folder.id"
v-model="(items[getItemIndex(folder)] as FolderClass)"
/>
</div>
<div class="mt-3 flex flex-wrap gap-3">
<!-- prettier-ignore-attribute -->
<File
v-for="file in files"
:key="file.id"
v-model="(items[getItemIndex(file)] as FileClass)"
/>
</div>
</template>
</template>

<script setup lang="ts">
import { computed, ref, type PropType } from 'vue';
import NoFiles from './NoFiles.vue';
import Folder from './Folder.vue';
import File from './File.vue';
import { ItemClass } from '@lib/items/items';
import { ItemFactory } from '@lib/items/factory';
import { FolderClass, type FolderType } from '@lib/items/folders';
import { FileClass } from '@lib/items/files';
import { api } from '@lib/helpers';
const props = defineProps({
modelValue: {
type: Object as PropType<FolderType>,
required: false,
},
});
/**
* Items
*/
const items = ref<ItemClass[]>([]);
getItems();
function getItems() {
fetch(api(`item${FolderClass.isFolder(props.modelValue) ? `/${props.modelValue.id}` : ''}`), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
})
.then(async (response) => {
if (!response.ok) {
if (response.status >= 400 && response.status < 500) {
throw new Error((await response.json()).error);
}
throw new Error(await response.text());
}
const rawItems = await response.json();
for (const rawItem of rawItems) {
if (!ItemClass.isItem(rawItem)) continue;
const item = ItemFactory.getItemFromObject(rawItem);
if (item === null) continue;
items.value.push(item);
}
return items.value;
})
.catch((error) => {
console.error('Error:', error);
});
}
function getItemIndex(item: ItemClass) {
return items.value.findIndex((i) => i.id === item.id);
}
/**
* Folders
*/
const folders = computed(() => {
return items.value.filter((item) => item instanceof FolderClass) as FolderClass[];
});
/**
* Files
*/
const files = computed(() => {
return items.value.filter((item) => item instanceof FileClass) as FileClass[];
});
</script>
60 changes: 60 additions & 0 deletions src/components/files/Folder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<ContextMenu>
<template v-slot:content>
<a :href="url(`u/folder/${modelValue.id}`)">
<button
type="button"
:class="classes"
class="h-[40px] rounded-lg bg-gradient-to-br px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-gradient-to-bl focus:outline-none focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800"
>
{{ modelValue.name }}
</button>
</a>
</template>
<template v-slot:menu>
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
<li>
<a
:href="url(`u/folder/${modelValue.id}`)"
target="_blank"
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
>Open in new tab</a
>
</li>
<li>
<a
href="#"
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
>Change Name</a
>
</li>
</ul>
<div class="py-2">
<a
href="#"
class="block px-4 py-2 text-sm text-red-500 hover:bg-gray-100 dark:text-red-500 dark:hover:bg-gray-600"
>Delete</a
>
</div>
</template>
</ContextMenu>
</template>

<script setup lang="ts">
import { type PropType, computed } from 'vue';
import { FolderClass } from '@lib/items/folders';
import { url } from '@lib/helpers';
import ContextMenu from './ContextMenu.vue';
const props = defineProps({
modelValue: {
type: Object as PropType<FolderClass>,
required: true,
},
});
defineEmits(['update:modelValue']);
const classes = computed(() => {
return props.modelValue.colorClass;
});
</script>
35 changes: 35 additions & 0 deletions src/components/files/NoFiles.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<div class="flex w-full items-center justify-center">
<label
for="dropzone-file"
class="dark:hover:bg-bray-800 flex h-64 w-full cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-700 dark:hover:border-gray-500 dark:hover:bg-gray-600"
>
<div class="flex flex-col items-center justify-center pb-6 pt-5">
<svg
class="mb-4 h-8 w-8 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 16"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
/>
</svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
<span class="font-semibold">Click to upload</span> or drag and drop
</p>
<p class="text-xs text-gray-500 dark:text-gray-400">
SVG, PNG, JPG or GIF (MAX. 800x400px)
</p>
</div>
<input id="dropzone-file" type="file" class="hidden" />
</label>
</div>
</template>

<script setup lang="ts"></script>
2 changes: 1 addition & 1 deletion src/lang/da.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default {
openUserMenu: 'Åben brugermenu',
},
link: {
dashboard: 'Hjem',
myfiles: 'Mine Filer',
settings: 'Indstillinger',
signOut: 'Log ud',
},
Expand Down
2 changes: 1 addition & 1 deletion src/lang/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default {
openUserMenu: 'Open user menu',
},
link: {
dashboard: 'Dashboard',
myfiles: 'My Files',
settings: 'Settings',
signOut: 'Sign out',
},
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/SideBar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { t } from '@lib/i18n';
d="M12 2.252A8.014 8.014 0 0117.748 8H12V2.252z"></path></svg
>
<span class="ml-3" sidebar-toggle-item
>{t('layout.link.dashboard', Astro.locals.currentLocale)}</span
>{t('layout.link.myfiles', Astro.locals.currentLocale)}</span
>
</a>
</li>
Expand Down
13 changes: 13 additions & 0 deletions src/lib/items/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { FileClass } from './files';
import { FolderClass } from './folders';
import { type ItemType } from './items';

export class ItemFactory {
static getItemFromObject(object: ItemType) {
if (FolderClass.isFolder(object)) return new FolderClass(object);
if (FileClass.isFile(object)) return new FileClass(object);

console.error('ItemClass.getItemFromObject: Invalid object');
return null;
}
}
Loading

0 comments on commit 4df7a9f

Please sign in to comment.