Skip to content

Commit

Permalink
#16 - Refactored modal
Browse files Browse the repository at this point in the history
frederikpyt committed Oct 9, 2023
1 parent 7c5509f commit 237a5ac
Showing 11 changed files with 195 additions and 123 deletions.
28 changes: 23 additions & 5 deletions src/components/base/confirmModal.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<BaseModal @close="emit('close')" class="text-center">
<BaseModal ref="modal" class="text-center">
<svg
class="mx-auto mb-4 h-12 w-12 text-gray-400 dark:text-gray-200"
aria-hidden="true"
@@ -21,7 +21,10 @@
<div class="flex">
<button
type="button"
@click.prevent="$emit('confirm')"
@click.prevent="
modal?.close();
$emit('confirm');
"
:class="
classes +
' ml-auto mr-2 inline-flex items-center rounded-lg px-5 py-2.5 text-center text-sm font-medium text-white focus:outline-none focus:ring-4'
@@ -31,7 +34,7 @@
</button>
<button
type="button"
@click.prevent="$emit('close')"
@click.prevent="modal?.close()"
class="mr-auto rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:border-gray-500 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white dark:focus:ring-gray-600"
>
{{ t('noCancel') }}
@@ -47,22 +50,37 @@ export enum ConfirmModalType {
</script>

<script setup lang="ts">
import { computed, type PropType } from 'vue';
import { computed, ref, type PropType } from 'vue';
import BaseModal from '@components/base/modal.vue';
import { t } from '@lib/i18n';
const emit = defineEmits(['close', 'confirm']);
const emit = defineEmits(['confirm']);
const props = defineProps({
type: {
type: String as PropType<ConfirmModalType>,
required: true,
},
});
defineExpose({
open,
close,
});
const modal = ref<InstanceType<typeof BaseModal>>();
const classes = computed(() => {
switch (props.type) {
case ConfirmModalType.Danger:
return 'bg-red-600 hover:bg-red-800 focus:ring-red-300 dark:focus:ring-red-800';
}
});
function open() {
modal.value?.open();
}
function close() {
modal.value?.close(false);
}
</script>
31 changes: 27 additions & 4 deletions src/components/base/modal.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<div
v-if="showModal"
tabindex="-1"
aria-hidden="true"
class="fixed left-0 right-0 top-0 z-50 flex h-full max-h-full w-full items-center justify-center overflow-y-auto overflow-x-hidden bg-black bg-opacity-50 p-4 md:inset-0"
@@ -11,7 +12,7 @@
<button
type="button"
class="absolute right-2.5 top-3 ml-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
@click="emit('close')"
@click="close()"
>
<svg
class="h-3 w-3"
@@ -39,15 +40,37 @@
</template>

<script setup lang="ts">
import { onClickOutside, onKeyDown } from '@vueuse/core';
import { ref } from 'vue';
import { onClickOutside, onKeyDown } from '@vueuse/core';
import { isModalOpen } from '@stores/modal';
const showModal = ref(false);
const emit = defineEmits(['close']);
defineExpose({
open,
close,
});
const modal = ref<HTMLElement>();
onClickOutside(modal, () => emit('close'));
onClickOutside(modal, () => close());
onKeyDown('Escape', () => {
emit('close');
close();
});
function open() {
isModalOpen.set(true);
showModal.value = true;
}
function close(emitClose: boolean = true) {
isModalOpen.set(false);
showModal.value = false;
if (emitClose) {
emit('close');
}
}
</script>
40 changes: 20 additions & 20 deletions src/components/item/Browser.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<template>
<div
class="relative h-full w-full px-4 pt-6"
v-on:contextmenu.capture.prevent="fileBrowserContextMenu?.openMenu"
>
<div class="relative h-full w-full px-4 pt-6" v-on:contextmenu.capture="openContextMenu">
<!-- Files & Folders -->
<NoFiles v-if="hasItemsLoaded && !Object.values(items).length" />
<template v-else-if="items">
@@ -26,10 +23,7 @@
<li>
<a
href="javascript:void(0)"
@click="
showCreateFolderModal = true;
fileBrowserContextMenu?.closeMenu();
"
@click="createFolderModal?.open()"
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
>{{ t('fileBrowser.folder.createFolder') }}</a
>
@@ -50,18 +44,14 @@
</li>
</ul>
</ContextMenu>
</div>

<!-- Modals -->
<CreateFolderModal
v-if="showCreateFolderModal"
:parentFolder="props.modelValue"
@close="showCreateFolderModal = false"
/>

<!-- Toasts -->
<div class="fixed right-5 top-24 z-50 flex w-full max-w-xs flex-col">
<BaseToast v-for="toast in toasts" :type="toast.type">{{ toast.message }}</BaseToast>
</div>
<!-- Modals -->
<CreateFolderModal ref="createFolderModal" :parentFolder="props.modelValue" />

<!-- Toasts -->
<div class="fixed right-5 top-24 z-50 flex w-full max-w-xs flex-col">
<BaseToast v-for="toast in toasts" :type="toast.type">{{ toast.message }}</BaseToast>
</div>
</template>
<script setup lang="ts">
@@ -80,6 +70,7 @@ import { FileClass } from '@lib/items/files';
import ContextMenu from '@components/base/contextMenu.vue';
import BaseToast, { ToastType } from '@components/base/toast.vue';
import { t } from '@lib/i18n';
import { isModalOpen } from '@stores/modal';
const props = defineProps({
modelValue: {
@@ -90,6 +81,15 @@ const props = defineProps({
const fileBrowserContextMenu = ref<InstanceType<typeof ContextMenu>>();
function openContextMenu(e: MouseEvent) {
if (isModalOpen.get()) {
return;
}
e.preventDefault();
fileBrowserContextMenu?.value?.openMenu(e);
}
// TODO: Fix toasts
const toasts = ref<{ message: string; type: ToastType }[]>([]);
@@ -143,7 +143,7 @@ async function getItems() {
/**
* Folders
*/
const showCreateFolderModal = ref(false);
const createFolderModal = ref<InstanceType<typeof CreateFolderModal>>();
const folders = computed(() => {
return Object.values(items.value).filter((item) => item instanceof FolderClass) as FolderClass[];
32 changes: 23 additions & 9 deletions src/components/item/file/EditModal.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<template>
<BaseModal @close="closeUpdateFileModal">
<BaseModal ref="modal" @close="close">
<h3 class="mb-4 text-xl font-medium">{{ t('fileBrowser.file.renameFile') }}</h3>
<form class="space-y-6" @submit.prevent="updateFile" ref="form">
<BaseInput id="fileName" type="text" v-model="file.name" :required="true">{{
t('fileBrowser.file.name')
}}</BaseInput>
<BaseInput
id="fileName"
type="text"
v-model="file.name"
:required="true"
:errors="errorObject?.errors.name"
>{{ t('fileBrowser.file.name') }}</BaseInput
>
<BaseButton type="submit" :color="ButtonColor.Primary">{{
t('fileBrowser.folder.edit')
}}</BaseButton>
@@ -23,14 +28,20 @@ import ErrorAlert, { type ErrorObject } from '@components/base/errorAlert.vue';
import { t } from '@lib/i18n';
import type { FileClass } from '@lib/items/files';
const emit = defineEmits(['close']);
const props = defineProps({
file: {
type: Object as PropType<FileClass>,
required: true,
},
});
defineExpose({
open,
close,
});
const modal = ref<InstanceType<typeof BaseModal>>();
const file = ref<{ name: string }>({
name: props.file.name,
});
@@ -54,15 +65,18 @@ async function updateFile() {
// TODO: Show success toast
closeUpdateFileModal();
close();
} catch (e) {}
}
function closeUpdateFileModal() {
function open() {
modal.value?.open();
}
function close() {
file.value = {
name: props.file.name,
};
emit('close');
modal.value?.close(false);
}
</script>
29 changes: 10 additions & 19 deletions src/components/item/file/File.vue
Original file line number Diff line number Diff line change
@@ -18,8 +18,7 @@
<a
href="javascript:void(0)"
@click="
showEditModal = true;
fileContextMenu?.closeMenu();
editFileModal?.open();
"
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
>{{ t('fileBrowser.file.rename') }}</a
@@ -39,26 +38,20 @@
<a
href="javascript:void(0)"
@click="
showDeleteModal = true;
fileContextMenu?.closeMenu();
deleteFileModal?.open();
"
class="block px-4 py-2 text-sm text-red-500 hover:bg-gray-100 dark:text-red-500 dark:hover:bg-gray-600"
>{{ t('fileBrowser.file.delete') }}</a
>
</div>
</ContextMenu>

<!-- Modals -->
<BaseConfirmModal
v-if="showDeleteModal"
:type="ConfirmModalType.Danger"
@confirm="deleteFile"
@close="showDeleteModal = false"
>
{{ t('fileBrowser.file.areYouSureYouWantToDeleteThisFile') }}</BaseConfirmModal
>
<EditFileModal v-if="showEditModal" :file="modelValue" @close="showEditModal = false" />
</div>

<!-- Modals -->
<BaseConfirmModal ref="deleteFileModal" :type="ConfirmModalType.Danger" @confirm="deleteFile">
{{ t('fileBrowser.file.areYouSureYouWantToDeleteThisFile') }}</BaseConfirmModal
>
<EditFileModal ref="editFileModal" :file="modelValue" />
</template>

<script setup lang="ts">
@@ -80,7 +73,7 @@ const props = defineProps({
},
});
const showDeleteModal = ref(false);
const deleteFileModal = ref<InstanceType<typeof BaseConfirmModal>>();
async function deleteFile() {
try {
await props.modelValue.delete();
@@ -93,9 +86,7 @@ async function deleteFile() {
// TODO: Show error toast
}
showDeleteModal.value = false;
}
const showEditModal = ref(false);
const editFileModal = ref<InstanceType<typeof EditFileModal>>();
</script>
Loading

0 comments on commit 237a5ac

Please sign in to comment.