Skip to content

Commit

Permalink
Merge pull request #37 from HF6-PROJECT/ara-20
Browse files Browse the repository at this point in the history
feat: Added Toast container
  • Loading branch information
Anders164a authored Oct 18, 2023
2 parents 087d829 + 3ba9dda commit b3db8fb
Show file tree
Hide file tree
Showing 20 changed files with 403 additions and 136 deletions.
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.1",
"@types/uuid": "^9.0.5",
"dotenv": "^16.3.1",
"prettier": "^3.0.3",
"prettier-plugin-astro": "^0.12.0",
Expand Down
15 changes: 15 additions & 0 deletions src/components/ToastContainer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<div class="fixed right-5 top-24 z-50 flex w-full max-w-xs flex-col">
<BaseToast v-for="toast in toasts" :key="(toast as Toast).id" :toast="toast as Toast">{{
toast?.message
}}</BaseToast>
</div>
</template>

<script setup lang="ts">
import BaseToast from '@components/base/toast.vue';
import { toastsStore, type Toast } from '@stores/toasts';
import { useStore } from '@nanostores/vue';
const toasts = useStore(toastsStore);
</script>
210 changes: 120 additions & 90 deletions src/components/base/toast.vue
Original file line number Diff line number Diff line change
@@ -1,101 +1,111 @@
<template>
<div
v-if="showToast"
class="mb-4 flex w-full max-w-xs items-center rounded-lg bg-white p-4 text-gray-500 shadow dark:bg-gray-800 dark:text-gray-400"
class="mb-4 flex w-full max-w-xs flex-col items-center rounded-lg bg-white text-gray-500 shadow dark:bg-gray-800 dark:text-gray-400"
role="alert"
@mouseenter="isMouseOver = true"
@mouseleave="isMouseOver = false"
>
<div
v-if="props.type === ToastType.Success"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-green-100 text-green-500 dark:bg-green-800 dark:text-green-200"
>
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
<div class="flex w-full p-4">
<div
v-if="props.toast.type === ToastType.Success"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-green-100 text-green-500 dark:bg-green-800 dark:text-green-200"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"
/>
</svg>
<span class="sr-only">Check icon</span>
</div>
<div
v-else-if="props.type === ToastType.Danger"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100 text-red-500 dark:bg-red-800 dark:text-red-200"
>
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"
/>
</svg>
<span class="sr-only">Check icon</span>
</div>
<div
v-else-if="props.toast.type === ToastType.Danger"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100 text-red-500 dark:bg-red-800 dark:text-red-200"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 11.793a1 1 0 1 1-1.414 1.414L10 11.414l-2.293 2.293a1 1 0 0 1-1.414-1.414L8.586 10 6.293 7.707a1 1 0 0 1 1.414-1.414L10 8.586l2.293-2.293a1 1 0 0 1 1.414 1.414L11.414 10l2.293 2.293Z"
/>
</svg>
<span class="sr-only">Error icon</span>
</div>
<div
v-else-if="props.type === ToastType.Warning"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-orange-100 text-orange-500 dark:bg-orange-700 dark:text-orange-200"
>
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 11.793a1 1 0 1 1-1.414 1.414L10 11.414l-2.293 2.293a1 1 0 0 1-1.414-1.414L8.586 10 6.293 7.707a1 1 0 0 1 1.414-1.414L10 8.586l2.293-2.293a1 1 0 0 1 1.414 1.414L11.414 10l2.293 2.293Z"
/>
</svg>
<span class="sr-only">Error icon</span>
</div>
<div
v-else-if="props.toast.type === ToastType.Warning"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-orange-100 text-orange-500 dark:bg-orange-700 dark:text-orange-200"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z"
/>
</svg>
<span class="sr-only">Warning icon</span>
</div>
<div
v-else-if="props.type === ToastType.Info"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100 text-blue-500 dark:bg-blue-700 dark:text-blue-200"
>
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z"
/>
</svg>
<span class="sr-only">Warning icon</span>
</div>
<div
v-else-if="props.toast.type === ToastType.Info"
class="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100 text-blue-500 dark:bg-blue-700 dark:text-blue-200"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z"
/>
</svg>
<span class="sr-only">Info icon</span>
</div>
<div class="ml-3 text-sm font-normal"><slot /></div>
<button
type="button"
class="-mx-1.5 -my-1.5 ml-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-white p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-900 focus:ring-2 focus:ring-gray-300 dark:bg-gray-800 dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white"
aria-label="Close"
@click="showToast = false"
>
<span class="sr-only">Close</span>
<svg
class="h-3 w-3"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 14"
<svg
class="h-5 w-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z"
/>
</svg>
<span class="sr-only">Info icon</span>
</div>
<div class="ml-3 text-sm font-normal"><slot /></div>
<button
type="button"
class="-mx-1.5 -my-1.5 ml-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-white p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-900 focus:ring-2 focus:ring-gray-300 dark:bg-gray-800 dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white"
aria-label="Close"
@click="closeToast()"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
/>
</svg>
</button>
<span class="sr-only">Close</span>
<svg
class="h-3 w-3"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 14"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
/>
</svg>
</button>
</div>
<div v-if="showToast" class="h-2.5 w-full rounded-full bg-gray-200 dark:bg-gray-700">
<div
class="h-2.5 rounded-full bg-gray-300 dark:bg-gray-600"
:style="{ width: value + '%' }"
></div>
</div>
</div>
</template>

Expand All @@ -110,12 +120,32 @@ export enum ToastType {

<script setup lang="ts">
import { ref, type PropType } from 'vue';
import { removeToast, type Toast } from '@stores/toasts';
const showToast = ref(true);
const isMouseOver = ref(false);
const value = ref(100);
const timer = setInterval(() => {
if (isMouseOver.value) return;
value.value -= 0.2;
if (value.value < 0) {
value.value = 100;
clearInterval(timer);
closeToast();
}
}, 10);
function closeToast() {
showToast.value = false;
removeToast(props.toast);
}
const props = defineProps({
type: {
type: String as PropType<ToastType>,
toast: {
type: Object as PropType<Toast>,
required: true,
},
});
Expand Down
31 changes: 13 additions & 18 deletions src/components/item/Browser.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<template>
<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" :modelValue="modelValue" />
<NoFiles
v-if="hasItemsLoaded && !Object.values(items).length"
:modelValue="modelValue"
:user="user"
/>
<template v-else-if="items">
<div class="flex flex-wrap gap-3">
<!-- prettier-ignore-attribute -->
Expand Down Expand Up @@ -61,11 +65,6 @@
<!-- Modals -->
<CreateFolderModal ref="createFolderModal" :parentFolder="props.modelValue" />
<CreateDocsModal ref="createDocsModal" :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">
Expand All @@ -79,11 +78,12 @@ import { fetchFromApi } from '@lib/helpers';
// Stores
import { useStore } from '@nanostores/vue';
import { addItem, removeItem, itemsStore } from '@stores/items';
import { addToast } from '@stores/toasts';
import { isModalOpen } from '@stores/modal';
// Components
import ContextMenu from '@components/base/contextMenu.vue';
import BaseToast, { ToastType } from '@components/base/toast.vue';
import { ToastType } from '@components/base/toast.vue';
const props = defineProps({
modelValue: {
Expand All @@ -107,9 +107,6 @@ function openContextMenu(e: MouseEvent) {
fileBrowserContextMenu?.value?.openMenu(e);
}
// TODO: Fix toasts
const toasts = ref<{ message: string; type: ToastType }[]>([]);
/**
* Items
*/
Expand Down Expand Up @@ -197,8 +194,8 @@ async function uploadFiles(e: Event) {
try {
await FileClass.create(file, props.modelValue ?? null);
} catch (error) {
toasts.value.push({
message: `Failed to upload file ${file.name}`,
addToast({
message: t('fileBrowser.file.toast.create.failed') + ' ' + file.name,
type: ToastType.Danger,
});
}
Expand All @@ -208,7 +205,7 @@ async function uploadFiles(e: Event) {
/**
* Docs
*/
import { DocsClass, type DocsType } from '@lib/items/docs';
import { DocsClass } from '@lib/items/docs';
import Docs from './docs/Docs.vue';
import CreateDocsModal from './docs/CreateModal.vue';
Expand All @@ -233,11 +230,9 @@ channel.bind('update', (data: ItemType) => {
const isNew = items.value[item.id] === undefined;
const isOwner = item.ownerId === props.user.id;
if (isNew && isOwner) {
// TODO: Fix toasts
toasts.value.push({
message: `${item.name} has been created`,
if (isNew && isOwner && item instanceof FileClass) {
addToast({
message: item.name + ' ' + t('fileBrowser.file.toast.create.success'),
type: ToastType.Success,
});
}
Expand Down
Loading

0 comments on commit b3db8fb

Please sign in to comment.