added toasts

This commit is contained in:
2023-02-26 17:48:43 +10:00
parent 55ec88e11f
commit cff787f541
4 changed files with 221 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
<template>
<div ref="toast" :class="['sm-toast', type]" :style="styles">
<div class="sm-toast-inner">
<h3 v-if="title && title.length > 0">
{{ title }}
</h3>
<p>{{ content }}</p>
<ion-icon name="close-outline" @click="handleClickClose" />
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { useToastStore } from "../store/ToastStore";
const props = defineProps({
id: {
type: Number,
required: true,
},
title: {
type: String,
default: "",
required: false,
},
type: {
type: String,
default: "primary",
required: false,
},
content: {
type: String,
required: true,
},
});
const toastStore = useToastStore();
const toast = ref(null);
let height = 40;
let hideTimeoutID: number | null = null;
const styles = ref({
opacity: 0,
marginTop: "40px",
});
const handleClickClose = () => {
if (hideTimeoutID != null) {
window.clearTimeout(hideTimeoutID);
hideTimeoutID = null;
}
removeToast();
};
const removeToast = () => {
styles.value.opacity = 0;
styles.value.marginTop = `-${height}px`;
window.setTimeout(() => {
toastStore.clearToast(props.id);
}, 500);
};
onMounted(() => {
window.setTimeout(() => {
styles.value.opacity = 1;
styles.value.marginTop = 0;
if (toast.value != null) {
const styles = window.getComputedStyle(toast.value);
const marginBottom = parseFloat(styles.marginBottom);
height = toast.value.offsetHeight + parseFloat(marginBottom) || 0;
}
hideTimeoutID = window.setTimeout(() => {
hideTimeoutID = null;
removeToast();
}, 8000);
}, 200);
});
</script>
<style lang="scss">
.sm-toast {
position: relative;
font-size: 70%;
background-color: #fff;
padding: map-get($spacer, 2) map-get($spacer, 2) map-get($spacer, 2)
map-get($spacer, 2);
border: 1px solid #fff;
border-radius: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.25);
margin-bottom: 1rem;
border: 1px solid $border-color;
transition: opacity 0.2s ease-in, margin 0.2s ease-in;
.sm-toast-inner {
border-left: 6px solid $primary-color;
padding: map-get($spacer, 1) map-get($spacer, 4) map-get($spacer, 1)
map-get($spacer, 2);
max-width: 250px;
}
h3 {
margin-top: 0;
}
p {
margin-bottom: 0;
line-height: 1rem;
}
ion-icon {
font-size: 1.25rem;
position: absolute;
top: 15px;
right: 15px;
color: $font-color;
cursor: pointer;
transition: color 0.2s ease-in-out;
&:hover {
color: $danger-color;
}
}
&.success .sm-toast-inner {
border-left-color: $success-color;
}
&.danger .sm-toast-inner {
border-left-color: $danger-color;
}
}
</style>

View File

@@ -0,0 +1,30 @@
<template>
<div class="sm-toast-container">
<SMToast
v-for="toast of toastStore.toasts"
:id="toast.id"
:key="toast.id"
:type="toast.type"
:title="toast.title"
:content="toast.content" />
</div>
</template>
<script setup lang="ts">
import { useToastStore } from "../store/ToastStore";
import SMToast from "./SMToast.vue";
const toastStore = useToastStore();
</script>
<style lang="scss">
.sm-toast-container {
position: fixed;
height: 2px;
top: 4rem;
right: 1rem;
height: 100%;
z-index: 3000;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,54 @@
import { defineStore } from "pinia";
export interface ToastOptions {
id?: number;
title?: string;
content: string;
type?: string;
}
export interface ToastItem {
id: number;
title: string;
content: string;
type: string;
}
export interface ToastStore {
toasts: ToastItem[];
}
export const defaultToastItem: ToastItem = {
id: 0,
title: "",
content: "",
type: "primary",
};
export const useToastStore = defineStore({
id: "toasts",
state: (): ToastStore => ({
toasts: [],
}),
actions: {
addToast(toast: ToastOptions) {
if (!toast.id || toast.id == 0) {
toast.id =
Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
}
toast.title = toast.title || defaultToastItem.title;
toast.type = toast.type || defaultToastItem.type;
this.toasts.push(toast);
console.log(this.toasts[0].title);
},
clearToast(id: number) {
this.toasts = this.toasts.filter(
(item: ToastItem) => item.id !== id
);
},
},
});

View File

@@ -9,6 +9,7 @@
</main>
<SMFooter />
<SMProgress />
<SMToastList />
<DialogWrapper :transition-attrs="{ name: 'fade' }" />
</template>
@@ -16,6 +17,7 @@
import SMNavbar from "../components/SMNavbar.vue";
import SMFooter from "../components/SMFooter.vue";
import SMProgress from "../components/SMProgress.vue";
import SMToastList from "../components/SMToastList.vue";
import { DialogWrapper } from "vue3-promise-dialog";
</script>