Added a modal window

This commit is contained in:
2021-08-08 11:42:56 +03:00
parent f124154dae
commit 75cf8ef3aa
6 changed files with 222 additions and 65 deletions

94
src/components/Modal.vue Normal file
View File

@@ -0,0 +1,94 @@
<template>
<div
class="modal"
:class="{hidden: !active}"
>
<div class="bg" @click="close" />
<div class="window">
<div class="close" @click="close">×</div>
<slot />
</div>
</div>
</template>
<script>
export default {
data() {
return {
active: false,
};
},
methods: {
open() {
this.active = true;
},
close() {
this.active = false;
},
},
};
</script>
<style lang="scss" scoped>
.modal {
position: fixed;
transition: all .4s;
.bg {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(90, 90, 90, 0.28);
backdrop-filter: blur(12px);
}
.window {
z-index: 1000;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 16px;
border-radius: 16px;
background: $lighter;
color: $dark;
width: 340px;
.close {
position: absolute;
top: 16px;
right: 16px;
cursor: pointer;
}
}
&.hidden {
opacity: 0;
pointer-events: none;
}
}
@media screen and (max-width: $max-width) {
.modal {
.window {
left: 0;
width: 100%;
bottom: 0;
top: unset;
box-sizing: border-box;
border-radius: 16px 16px 0 0;
transform: translate(0, 0);
transition: transform .4s;
}
&.hidden {
.window {
opacity: 1;
transform: translate(0, 100%);
}
}
}
}
</style>

View File

@@ -5,15 +5,16 @@
v-model="newCategory"
placeholder="New category"
@keypress.enter="addCategory"
@mouseenter="newCategory=''"
@mouseenter="newCategory = ''"
@mouseleave="blurInput"
/>
<div class="categories">
<div
class="category"
v-for="category in categories" :key="category"
:style="{background: stringToColor(category)}"
:class="{selected: selectedCategory===category}"
v-for="category in categories"
:key="category"
:style="{ background: stringToColor(category) }"
:class="{ selected: selectedCategory === category }"
@click="selectCategory(category)"
>
{{ category }}
@@ -75,7 +76,7 @@ export default {
top: 0;
left: 0;
width: 100%;
border-bottom: 1px solid $light;
//border-bottom: 1px solid $light;
height: 48px;
background: $dark;
display: flex;
@@ -89,20 +90,21 @@ export default {
padding: 4px 12px;
width: 32px;
box-sizing: border-box;
transition: width .6s;
transition: width 0.6s;
&:hover {
width: 220px
width: 220px;
}
}
.categories {
display: flex;
margin-left: 32px;
overflow-x: scroll;
.category {
margin-right: 16px;
border: 3px solid transparent;
border: 3px solid $dark;
&.selected {
border: 3px double $dark;
@@ -110,4 +112,10 @@ export default {
}
}
}
@media screen and (max-width: $max-width) {
.category-bar {
padding: 0 0 0 8px;
}
}
</style>

View File

@@ -1,12 +1,7 @@
$dark-blue: #05263B;
$misty-blue: #AEB8C4;
$blue-grotto: #163B50;
$slate: #9CA6B8;
$dark: $blue-grotto;
$darker: $dark-blue;
$light: $slate;
$lighter: $misty-blue;
$dark: #1f1f1f;
$darker: #121212;
$light: rgb(219, 219, 219);
$lighter: white;
$max-width: 1024px;

View File

@@ -4,6 +4,7 @@
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 16px;
color: $light;
background: $darker;
@@ -13,12 +14,24 @@
input, button {
border: none;
border-radius: 16px;
&::placeholder {
color: $dark;
opacity: 1;
}
}
.category {
border-radius: 16px;
padding: 4px 12px;
padding: 6px 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
color: $darker;
span {
margin-left: 4px;
}
}
}

View File

@@ -1,59 +1,69 @@
<template>
<div
class="task"
:class="{active: task.running}"
>
<div class="task" :class="{ active: task.running }">
<div class="time">
{{ formattedTime }}
</div>
<div class="toggle-state">
<div v-if="task.running" @click="stopTask">
<img src="@/assets/pause.svg">
<img src="@/assets/pause.svg" />
</div>
<div v-else @click="startTask">
<img src="@/assets/play.svg">
<img src="@/assets/play.svg" />
</div>
</div>
<div class="name">
{{ task.name }}
</div>
<div class="spacer"/>
<div
v-if="task.category"
class="category"
:style="{ background: stringToColor(task.category) }"
>
{{ task.category }}
<span @click="removeCategory">×</span>
</div>
<div
v-else
class="select-category"
>
Assign Category
<CategoryDropdown
<div class="optional-break">
<div class="name">
{{ task.name }}
</div>
<div
v-if="task.category"
class="category"
:style="{ background: stringToColor(task.category) }"
>
{{ task.category }}
<span @click="removeCategory">×</span>
</div>
<div v-else class="select-category" @click="$refs.categorySelect.open">
Assign Category
<!-- <CategoryDropdown
class="dropdown"
@selected="assignCategory"
/>
/> -->
</div>
</div>
<div
class="delete"
@click="removeTask"
>
<img src="@/assets/trash.svg">
<Modal ref="categorySelect" class="modal">
<div class="title">Assign Category</div>
<div
v-for="category in categories"
:key="category"
class="category"
:style="{ background: stringToColor(category) }"
@click="assignCategory(category)"
>
{{ category }}
</div>
</Modal>
<div class="delete" @click="removeTask">
<img src="@/assets/trash.svg" />
</div>
</div>
</template>
<script>
import { mapState } from 'vuex';
import toColor from '@/stringToColor';
import CategoryDropdown from './CategoryDropdown.vue';
// import CategoryDropdown from './CategoryDropdown.vue';
import Modal from '@/components/Modal.vue';
export default {
components: {
CategoryDropdown,
// CategoryDropdown,
Modal,
},
props: {
task: Object,
@@ -73,9 +83,13 @@ export default {
},
assignCategory(category) {
this.$store.commit('assignCategory', { name: this.task.name, category });
this.$refs.categorySelect.close();
},
removeCategory() {
this.$store.commit('assignCategory', { name: this.task.name, category: undefined });
this.$store.commit('assignCategory', {
name: this.task.name,
category: undefined,
});
},
stringToColor(str) {
return toColor(str);
@@ -106,6 +120,9 @@ export default {
return `${days}d ${hours}h ${mins}m`;
},
...mapState({
categories: (state) => state.categories,
}),
},
mounted() {
setInterval(() => {
@@ -118,12 +135,12 @@ export default {
<style lang="scss" scoped>
.task {
color: $lighter;
height: 42px;
min-height: 42px;
display: flex;
align-items: center;
.time {
width: 124px;
min-width: 104px;
}
.toggle-state {
@@ -146,37 +163,54 @@ export default {
}
}
.spacer {
.optional-break {
flex-grow: 1;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
align-items: center;
}
.select-category {
position: relative;
color: $darker;
color: $light;
background: $dark;
padding: 4px 12px;
border-radius: 16px;
cursor: pointer;
.dropdown {
display: none;
}
// .dropdown {
// display: none;
// }
&:hover {
.dropdown {
display: block;
}
}
// &:hover {
// .dropdown {
// display: block;
// }
// }
}
.delete {
margin-left: 16px;
cursor: pointer;
}
.modal {
.category {
margin-top: 12px;
border-radius: 24px !important;
padding: 12px 24px !important;
}
}
}
@keyframes pulse {
from {opacity: .3;}
to {opacity: 1;}
from {
opacity: 0.3;
}
to {
opacity: 1;
}
}
.task.active {
@@ -184,4 +218,16 @@ export default {
animation: pulse 1s infinite alternate;
}
}
@media screen and (max-width: $max-width) {
.task {
min-height: 64px;
margin-bottom: 16px;
.optional-break {
flex-direction: column;
height: 48px;
}
}
}
</style>

View File

@@ -55,6 +55,7 @@ export default {
.task-list {
max-width: $max-width;
margin: 32px auto;
padding: 0 8px;
.add-task {
width: 100%;