Files
seasoned/src/components/ui/SeasonedMessages.vue

174 lines
3.5 KiB
Vue

<template>
<transition-group name="fade">
<div
v-for="(message, index) in messages"
:key="generateMessageKey(index, message)"
class="card"
:class="message.type || 'warning'"
>
<span class="pinstripe"></span>
<div class="content">
<h2 class="title">
{{ message.title || titleFromType(message.type) }}
</h2>
<span v-if="message.message" class="message">{{
message.message
}}</span>
</div>
<button class="dismiss" @click="dismiss(Number(index))">X</button>
</div>
</transition-group>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from "vue";
import type {
ErrorMessageTypes,
IErrorMessage
} from "../../interfaces/IErrorMessage";
interface Props {
messages: IErrorMessage[];
}
interface Emit {
(e: "update:messages", messages: IErrorMessage[]);
}
const props = defineProps<Props>();
const emit = defineEmits<Emit>();
const defaultTitles = {
error: "Unexpected error",
warning: "Something went wrong",
success: "Success!",
undefined: "Something went wrong"
};
function titleFromType(type: ErrorMessageTypes) {
return defaultTitles[type];
}
function dismiss(index: number) {
const _messages = [...props.messages];
_messages.splice(index, 1);
emit("update:messages", _messages);
}
function generateMessageKey(
index: string | number | symbol,
errorMessage: IErrorMessage
) {
return `${String(index)}-${errorMessage.title}-${errorMessage.type}`;
}
</script>
<style lang="scss" scoped>
@import "src/scss/variables";
@import "src/scss/media-queries";
.fade-active {
transition: opacity 0.4s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
.card {
width: 100%;
max-width: 35rem;
display: flex;
margin-top: 0.8rem;
color: $text-color-70;
.content {
margin: 0.4rem 1.2rem;
width: 100%;
.title {
font-weight: 300;
letter-spacing: 0.25px;
margin: 0;
font-size: 1.3rem;
color: $text-color;
transition: color 0.5s ease;
}
.message {
font-weight: 400;
font-size: 1.2rem;
color: $text-color-70;
transition: color 0.5s ease;
margin-bottom: 0.2rem;
}
@include mobile-only {
margin: 6px 6px;
line-height: 1.3rem;
h2 {
font-size: 1.1rem;
}
span {
font-size: 0.9rem;
}
}
}
.pinstripe {
width: 0.5rem;
background-color: $color-error-highlight;
}
.dismiss {
position: relative;
-webkit-appearance: none;
-moz-appearance: none;
background-color: transparent;
border: unset;
font-size: 18px;
cursor: pointer;
top: 0;
float: right;
height: 1.5rem;
width: 1.5rem;
padding: 0;
margin-top: 0.5rem;
margin-right: 0.5rem;
color: $text-color-70;
transition: color 0.5s ease;
&:hover {
color: $text-color;
}
}
&.success {
background-color: $color-success;
.pinstripe {
background-color: $color-success-highlight;
}
}
&.error {
background-color: $color-error;
.pinstripe {
background-color: $color-error-highlight;
}
}
&.warning {
background-color: $color-warning;
.pinstripe {
background-color: $color-warning-highlight;
}
}
}
</style>