mirror of
https://github.com/KevinMidboe/seasoned.git
synced 2026-03-11 03:49:07 +00:00
Upgraded all components to vue 3 & typescript
This commit is contained in:
@@ -4,49 +4,43 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
darkmode: this.systemDarkModeEnabled()
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleDarkmode() {
|
||||
this.darkmode = !this.darkmode;
|
||||
document.body.className = this.darkmode ? "dark" : "light";
|
||||
},
|
||||
systemDarkModeEnabled() {
|
||||
const computedStyle = window.getComputedStyle(document.body);
|
||||
if (computedStyle["colorScheme"] != null) {
|
||||
return computedStyle.colorScheme.includes("dark");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
darkmodeToggleIcon() {
|
||||
return this.darkmode ? "🌝" : "🌚";
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
let darkmode = ref(systemDarkModeEnabled());
|
||||
const darkmodeToggleIcon = computed(() => {
|
||||
return darkmode.value ? "🌝" : "🌚";
|
||||
});
|
||||
|
||||
function toggleDarkmode() {
|
||||
darkmode.value = !darkmode.value;
|
||||
document.body.className = darkmode.value ? "dark" : "light";
|
||||
}
|
||||
|
||||
function systemDarkModeEnabled() {
|
||||
const computedStyle = window.getComputedStyle(document.body);
|
||||
if (computedStyle["colorScheme"] != null) {
|
||||
return computedStyle.colorScheme.includes("dark");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.darkToggle {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
cursor: pointer;
|
||||
position: fixed;
|
||||
margin-bottom: 1.5rem;
|
||||
margin-right: 2px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
.darkToggle {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
cursor: pointer;
|
||||
position: fixed;
|
||||
margin-bottom: 1.5rem;
|
||||
margin-right: 2px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,73 +10,76 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from "vuex";
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
computed: { ...mapGetters("hamburger", ["isOpen"]) },
|
||||
methods: { ...mapActions("hamburger", ["toggle"]) }
|
||||
};
|
||||
const store = useStore();
|
||||
|
||||
const isOpen = computed(() => store.getters["hamburger/isOpen"]);
|
||||
const toggle = () => {
|
||||
store.dispatch("hamburger/toggle");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/scss/media-queries";
|
||||
@import "src/scss/media-queries";
|
||||
|
||||
.nav__hamburger {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: var(--header-size);
|
||||
height: var(--header-size);
|
||||
cursor: pointer;
|
||||
border-left: 1px solid var(--background-color);
|
||||
background-color: var(--background-color-secondary);
|
||||
.nav__hamburger {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: var(--header-size);
|
||||
height: var(--header-size);
|
||||
cursor: pointer;
|
||||
border-left: 1px solid var(--background-color);
|
||||
background-color: var(--background-color-secondary);
|
||||
|
||||
@include tablet-min {
|
||||
display: none;
|
||||
}
|
||||
@include tablet-min {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bar {
|
||||
position: absolute;
|
||||
width: 23px;
|
||||
height: 1px;
|
||||
background-color: var(--text-color-70);
|
||||
transition: all 300ms ease;
|
||||
&:nth-child(1) {
|
||||
left: 16px;
|
||||
top: 17px;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
left: 16px;
|
||||
top: 25px;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 23px;
|
||||
height: 1px;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
}
|
||||
&:nth-child(3) {
|
||||
right: 15px;
|
||||
top: 33px;
|
||||
}
|
||||
}
|
||||
&.open {
|
||||
.bar {
|
||||
&:nth-child(1),
|
||||
&:nth-child(3) {
|
||||
width: 0;
|
||||
position: absolute;
|
||||
width: 23px;
|
||||
height: 1px;
|
||||
background-color: var(--text-color-70);
|
||||
transition: all 300ms ease;
|
||||
&:nth-child(1) {
|
||||
left: 16px;
|
||||
top: 17px;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
transform: rotate(-45deg);
|
||||
left: 16px;
|
||||
top: 25px;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 23px;
|
||||
height: 1px;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
}
|
||||
&:nth-child(2):after {
|
||||
transform: rotate(-90deg);
|
||||
background-color: var(--text-color-70);
|
||||
&:nth-child(3) {
|
||||
right: 15px;
|
||||
top: 33px;
|
||||
}
|
||||
}
|
||||
&.open {
|
||||
.bar {
|
||||
&:nth-child(1),
|
||||
&:nth-child(3) {
|
||||
width: 0;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
&:nth-child(2):after {
|
||||
transform: rotate(-90deg);
|
||||
background-color: var(--text-color-70);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,48 +1,72 @@
|
||||
<template>
|
||||
<div class="loader">
|
||||
<div :class="`loader type-${type}`">
|
||||
<i class="loader--icon">
|
||||
<i class="loader--icon-spinner" />
|
||||
</i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
TODO: fetch and display movie facts after 1.5 seconds while loading?
|
||||
|
||||
|
||||
--></template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from "vue";
|
||||
|
||||
enum LoaderHeightType {
|
||||
Page = "page",
|
||||
Section = "section"
|
||||
}
|
||||
|
||||
interface Props {
|
||||
type?: LoaderHeightType;
|
||||
}
|
||||
|
||||
const { type = LoaderHeightType.Page } = defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/variables";
|
||||
|
||||
.loader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 30vh;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.loader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 30vh;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&--icon {
|
||||
border: 2px solid $text-color-70;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
&.type-section {
|
||||
height: 15vh;
|
||||
}
|
||||
|
||||
&-spinner {
|
||||
&--icon {
|
||||
border: 2px solid $text-color-70;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
animation: load 1s linear infinite;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
&:after {
|
||||
border: 7px solid $green-90;
|
||||
border-radius: 50%;
|
||||
content: "";
|
||||
left: 8px;
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
|
||||
&-spinner {
|
||||
display: block;
|
||||
animation: load 1s linear infinite;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
&:after {
|
||||
border: 7px solid $green-90;
|
||||
border-radius: 50%;
|
||||
content: "";
|
||||
left: 8px;
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes load {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes load {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,26 +9,18 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
count: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
require: false
|
||||
},
|
||||
lineClass: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
top: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from "vue";
|
||||
|
||||
interface Props {
|
||||
count?: Number;
|
||||
lineClass?: String;
|
||||
top?: Number;
|
||||
}
|
||||
};
|
||||
|
||||
const { count = 1, lineClass = "", top = 0 } = defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/scss/loading-placeholder";
|
||||
@import "src/scss/loading-placeholder";
|
||||
</style>
|
||||
|
||||
@@ -8,77 +8,71 @@
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "seasonedButton",
|
||||
props: {
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
},
|
||||
fullWidth: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emit() {
|
||||
this.$emit("click");
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
|
||||
interface Props {
|
||||
active?: Boolean;
|
||||
fullWidth?: Boolean;
|
||||
}
|
||||
};
|
||||
|
||||
interface Emit {
|
||||
(e: "click");
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emit>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/media-queries";
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/media-queries";
|
||||
|
||||
button {
|
||||
display: inline-block;
|
||||
border: 1px solid $text-color;
|
||||
font-size: 11px;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
min-height: 45px;
|
||||
padding: 5px 10px 4px 10px;
|
||||
margin: 0;
|
||||
margin-right: 0.3rem;
|
||||
color: $text-color;
|
||||
background: $background-color-secondary;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
transition: background 0.5s ease, color 0.5s ease, border-color 0.5s ease;
|
||||
button {
|
||||
display: inline-block;
|
||||
border: 1px solid $text-color;
|
||||
font-size: 11px;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
min-height: 45px;
|
||||
padding: 5px 10px 4px 10px;
|
||||
margin: 0;
|
||||
margin-right: 0.3rem;
|
||||
color: $text-color;
|
||||
background: $background-color-secondary;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
transition: background 0.5s ease, color 0.5s ease, border-color 0.5s ease;
|
||||
|
||||
@include desktop {
|
||||
font-size: 0.8rem;
|
||||
padding: 6px 20px 5px 20px;
|
||||
}
|
||||
|
||||
&.fullwidth {
|
||||
font-size: 14px;
|
||||
width: 40%;
|
||||
|
||||
@include mobile {
|
||||
width: 60%;
|
||||
@include desktop {
|
||||
font-size: 0.8rem;
|
||||
padding: 6px 20px 5px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active {
|
||||
background: $text-color;
|
||||
color: $background-color;
|
||||
}
|
||||
&.fullwidth {
|
||||
font-size: 14px;
|
||||
width: 40%;
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
@include mobile {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active {
|
||||
background: $text-color;
|
||||
color: $background-color;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
background: $text-color;
|
||||
color: $background-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,134 +1,139 @@
|
||||
<template>
|
||||
<div class="group" :class="{ completed: value, focus }">
|
||||
<div class="group" :class="{ completed: modelValue, focus }">
|
||||
<component :is="inputIcon" v-if="inputIcon" />
|
||||
|
||||
<input
|
||||
class="input"
|
||||
:type="tempType || type"
|
||||
@input="handleInput"
|
||||
v-model="inputValue"
|
||||
:type="toggledType || type"
|
||||
:placeholder="placeholder"
|
||||
@keyup.enter="event => $emit('enter', event)"
|
||||
:value="modelValue"
|
||||
@input="handleInput"
|
||||
@keyup.enter="event => emit('enter', event)"
|
||||
@focus="focus = true"
|
||||
@blur="focus = false"
|
||||
/>
|
||||
|
||||
<i
|
||||
v-if="value && type === 'password'"
|
||||
v-if="modelValue && type === 'password'"
|
||||
@click="toggleShowPassword"
|
||||
@keydown.enter="toggleShowPassword"
|
||||
class="show noselect"
|
||||
tabindex="0"
|
||||
>{{ tempType == "password" ? "show" : "hide" }}</i
|
||||
>{{ toggledType == "password" ? "show" : "hide" }}</i
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconKey from "../../icons/IconKey";
|
||||
import IconEmail from "../../icons/IconEmail";
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineProps, defineEmits } from "vue";
|
||||
import IconKey from "@/icons/IconKey.vue";
|
||||
import IconEmail from "@/icons/IconEmail.vue";
|
||||
import IconBinoculars from "@/icons/IconBinoculars.vue";
|
||||
import type { Ref } from "vue";
|
||||
|
||||
export default {
|
||||
components: { IconKey, IconEmail },
|
||||
props: {
|
||||
placeholder: { type: String },
|
||||
type: { type: String, default: "text" },
|
||||
value: { type: String, default: undefined }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value || undefined,
|
||||
tempType: this.type,
|
||||
focus: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
inputIcon() {
|
||||
if (this.type === "password") return IconKey;
|
||||
if (this.type === "email") return IconEmail;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput(event) {
|
||||
if (this.value !== undefined) {
|
||||
this.$emit("update:value", this.inputValue);
|
||||
} else {
|
||||
this.$emit("change", this.inputValue, event);
|
||||
}
|
||||
},
|
||||
toggleShowPassword() {
|
||||
if (this.tempType === "text") {
|
||||
this.tempType = "password";
|
||||
} else {
|
||||
this.tempType = "text";
|
||||
}
|
||||
interface Props {
|
||||
modelValue: string;
|
||||
placeholder: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: "change");
|
||||
(e: "enter");
|
||||
(e: "update:modelValue", value: string);
|
||||
}
|
||||
|
||||
const { placeholder, type = "text", modelValue } = defineProps<Props>();
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
const toggledType: Ref<string> = ref(type);
|
||||
const focus: Ref<boolean> = ref(false);
|
||||
|
||||
const inputIcon = computed(() => {
|
||||
if (type === "password") return IconKey;
|
||||
if (type === "email") return IconEmail;
|
||||
if (type === "torrents") return IconBinoculars;
|
||||
return false;
|
||||
});
|
||||
|
||||
function handleInput(event: KeyboardEvent) {
|
||||
const target = event?.target as HTMLInputElement;
|
||||
if (!target) return;
|
||||
|
||||
emit("update:modelValue", target?.value);
|
||||
}
|
||||
|
||||
// Could we move this to component that injects ??
|
||||
function toggleShowPassword() {
|
||||
if (toggledType.value === "text") {
|
||||
toggledType.value = "password";
|
||||
} else {
|
||||
toggledType.value = "text";
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/media-queries";
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/media-queries";
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
max-width: 35rem;
|
||||
border: 1px solid var(--text-color-50);
|
||||
background-color: var(--background-color-secondary);
|
||||
.group {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
max-width: 35rem;
|
||||
border: 1px solid var(--text-color-50);
|
||||
background-color: var(--background-color-secondary);
|
||||
|
||||
&.completed,
|
||||
&.focus,
|
||||
&:hover,
|
||||
&:focus {
|
||||
border-color: var(--text-color);
|
||||
&.completed,
|
||||
&.focus,
|
||||
&:hover,
|
||||
&:focus {
|
||||
border-color: var(--text-color);
|
||||
|
||||
svg {
|
||||
fill: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: var(--text-color);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: var(--text-color-50);
|
||||
pointer-events: none;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
outline: none;
|
||||
background-color: var(--background-color-secondary);
|
||||
color: var(--text-color);
|
||||
font-weight: 100;
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
z-index: 3;
|
||||
border: none;
|
||||
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.show {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
right: 20px;
|
||||
z-index: 11;
|
||||
margin: auto 0;
|
||||
height: 100%;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-color-50);
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: var(--text-color-50);
|
||||
pointer-events: none;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
outline: none;
|
||||
background-color: var(--background-color-secondary);
|
||||
color: var(--text-color);
|
||||
font-weight: 100;
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
z-index: 3;
|
||||
border: none;
|
||||
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.show {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
right: 20px;
|
||||
z-index: 11;
|
||||
margin: auto 0;
|
||||
height: 100%;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-color-50);
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<transition-group name="fade">
|
||||
<div
|
||||
class="message"
|
||||
v-for="(message, index) in reversedMessages"
|
||||
class="card"
|
||||
v-for="(message, index) in messages"
|
||||
:key="`${index}-${message.title}-${message.type}}`"
|
||||
:class="message.type || 'warning'"
|
||||
>
|
||||
<span class="pinstripe"></span>
|
||||
<div>
|
||||
<div class="content">
|
||||
<h2 class="title">
|
||||
{{ message.title || defaultTitles[message.type] }}
|
||||
</h2>
|
||||
@@ -16,150 +16,149 @@
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<button class="dismiss" @click="clicked(message)">X</button>
|
||||
<button class="dismiss" @click="dismiss(index)">X</button>
|
||||
</div>
|
||||
</transition-group>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
messages: {
|
||||
required: true,
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultTitles: {
|
||||
error: "Unexpected error",
|
||||
warning: "Something went wrong",
|
||||
undefined: "Something went wrong"
|
||||
},
|
||||
localMessages: [...this.messages]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
reversedMessages() {
|
||||
return [...this.messages].reverse();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clicked(e) {
|
||||
const removedMessage = [...this.messages].filter(mes => mes !== e);
|
||||
this.$emit("update:messages", removedMessage);
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { defineProps, defineEmits } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
|
||||
interface IErrorMessage {
|
||||
title: string;
|
||||
message: string;
|
||||
type: "error" | "success" | "warning";
|
||||
}
|
||||
|
||||
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: "",
|
||||
undefined: "Something went wrong"
|
||||
};
|
||||
|
||||
function dismiss(index: number) {
|
||||
props.messages.splice(index, 1);
|
||||
emit("update:messages", [...props.messages]);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/media-queries";
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/media-queries";
|
||||
|
||||
.fade-enter-active {
|
||||
transition: opacity 0.4s;
|
||||
}
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
.fade-active {
|
||||
transition: opacity 0.4s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.message {
|
||||
width: 100%;
|
||||
max-width: 35rem;
|
||||
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: $text-color-70;
|
||||
|
||||
> div {
|
||||
margin: 10px 24px;
|
||||
.card {
|
||||
width: 100%;
|
||||
}
|
||||
max-width: 35rem;
|
||||
|
||||
.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: 300;
|
||||
display: flex;
|
||||
margin-top: 0.8rem;
|
||||
color: $text-color-70;
|
||||
transition: color 0.5s ease;
|
||||
margin: 0.2rem 0 0.5rem;
|
||||
}
|
||||
|
||||
@include mobile-only {
|
||||
> div {
|
||||
margin: 6px 6px;
|
||||
line-height: 1.3rem;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
width: 0.5rem;
|
||||
background-color: $color-error-highlight;
|
||||
}
|
||||
}
|
||||
|
||||
&.warning {
|
||||
background-color: $color-warning;
|
||||
.dismiss {
|
||||
position: relative;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background-color: transparent;
|
||||
border: unset;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
|
||||
.pinstripe {
|
||||
background-color: $color-warning-highlight;
|
||||
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>
|
||||
|
||||
@@ -4,83 +4,74 @@
|
||||
v-for="option in options"
|
||||
:key="option"
|
||||
class="toggle-button"
|
||||
@click="toggle(option)"
|
||||
:class="toggleValue === option ? 'selected' : null"
|
||||
@click="toggleTo(option)"
|
||||
:class="selected === option ? 'selected' : null"
|
||||
>
|
||||
{{ option }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
options: {
|
||||
Array,
|
||||
required: true
|
||||
},
|
||||
selected: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
toggleValue: this.selected || this.options[0]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggle(toggleValue) {
|
||||
this.toggleValue = toggleValue;
|
||||
if (this.selected !== undefined) {
|
||||
this.$emit("update:selected", toggleValue);
|
||||
this.$emit("change", toggleValue);
|
||||
} else {
|
||||
this.$emit("change", toggleValue);
|
||||
}
|
||||
}
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
|
||||
interface Props {
|
||||
options: string[];
|
||||
selected?: string;
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(e: "update:selected", selected: string);
|
||||
(e: "change");
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
function toggleTo(option: string) {
|
||||
emit("update:selected", option);
|
||||
emit("change");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "src/scss/variables";
|
||||
@import "src/scss/variables";
|
||||
|
||||
$background: $background-ui;
|
||||
$background-selected: $background-color-secondary;
|
||||
$background: $background-ui;
|
||||
$background-selected: $background-color-secondary;
|
||||
|
||||
.toggle-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow-x: scroll;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: $background;
|
||||
border: 2px solid $background;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid $background;
|
||||
border-right: 4px solid $background;
|
||||
|
||||
.toggle-button {
|
||||
font-size: 1rem;
|
||||
line-height: 1rem;
|
||||
font-weight: normal;
|
||||
padding: 0.5rem;
|
||||
border: 0;
|
||||
color: $text-color;
|
||||
.toggle-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow-x: scroll;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: $background;
|
||||
text-transform: capitalize;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
flex: 1 0 auto;
|
||||
border: 2px solid $background;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid $background;
|
||||
border-right: 4px solid $background;
|
||||
|
||||
&.selected {
|
||||
.toggle-button {
|
||||
font-size: 1rem;
|
||||
line-height: 1rem;
|
||||
font-weight: normal;
|
||||
padding: 0.5rem;
|
||||
border: 0;
|
||||
color: $text-color;
|
||||
background-color: $background-selected;
|
||||
border-radius: 8px;
|
||||
background-color: $background;
|
||||
text-transform: capitalize;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
flex: 1 0 auto;
|
||||
|
||||
&.selected {
|
||||
color: $text-color;
|
||||
background-color: $background-selected;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user