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

View File

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

View File

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

View File

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

View File

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