feat(inputs): Created inputs to the user create custom tasks.
Created inputs for task and sub-tasks (`Task/TaskInput.vue`, `SubTask/SubTaskInput.vue`), enabling the creation of custom tasks and sub-tasks; Removed debug buttons.
This commit is contained in:
@@ -95,7 +95,6 @@ export default Vue.extend({
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
font: inherit;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 5px solid currentColor;
|
||||
@@ -107,6 +106,7 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
.description {
|
||||
font-family: $secondary-font;
|
||||
margin: 1px 0;
|
||||
}
|
||||
}
|
||||
|
||||
171
src/components/SubTask/SubTaskInput.vue
Normal file
171
src/components/SubTask/SubTaskInput.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="alignInputContainerSubTask">
|
||||
<div class="subTaskInputContainer">
|
||||
<button class="addTask" @click="addSubTask(parent)">
|
||||
<IconAdd class="addIcon" />
|
||||
</button>
|
||||
<form class="subTaskInput">
|
||||
<input
|
||||
id="newSubTaskInputName"
|
||||
class="subTaskNameInput"
|
||||
type="text"
|
||||
name="subTaskNameInput"
|
||||
placeholder="Sub-Task Title..."
|
||||
maxlength="50"
|
||||
@keypress.enter="addSubTask(parent)"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import sm from '~/libs/storageManagement';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'SubTaskInput',
|
||||
props: {
|
||||
parent: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addSubTask: (parentId: number) => {
|
||||
if (sm.get('tasks') === undefined) sm.add('tasks', []);
|
||||
|
||||
const titleInput = document.querySelector(
|
||||
'input#newSubTaskInputName'
|
||||
) as HTMLInputElement;
|
||||
|
||||
const newTitle = titleInput.value;
|
||||
|
||||
if (!newTitle || newTitle === '') {
|
||||
if (!titleInput.className.includes(' warnTitle'))
|
||||
titleInput.className += ' warnTitle';
|
||||
|
||||
(
|
||||
document.querySelector(
|
||||
'input#newSubTaskInputName'
|
||||
) as HTMLInputElement
|
||||
).className = titleInput.className;
|
||||
|
||||
setTimeout(() => {
|
||||
(
|
||||
document.querySelector(
|
||||
'input#newSubTaskInputName'
|
||||
) as HTMLInputElement
|
||||
).className = titleInput.className.replace(' warnTitle', '');
|
||||
}, 1500);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const subTaskItem: SubTask = {
|
||||
description: newTitle,
|
||||
checked: false,
|
||||
};
|
||||
|
||||
const localTasks: Task[] = sm.get('tasks');
|
||||
const parentTask = localTasks[parentId];
|
||||
|
||||
if (!parentTask.subTasks) parentTask.subTasks = [];
|
||||
|
||||
parentTask.subTasks.push(subTaskItem);
|
||||
localTasks[parentId] = parentTask;
|
||||
|
||||
sm.set('tasks', localTasks);
|
||||
|
||||
(
|
||||
document.querySelector('input#newSubTaskInputName') as HTMLInputElement
|
||||
).value = '';
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~assets/styles/_variables.scss';
|
||||
@import '~assets/styles/_mixins.scss';
|
||||
|
||||
.alignInputContainerSubTask {
|
||||
@include center;
|
||||
justify-content: left;
|
||||
&,
|
||||
* {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
*::placeholder {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.subTaskInputContainer {
|
||||
@include center;
|
||||
width: 100%;
|
||||
|
||||
.addTask {
|
||||
$bg-color: #00000080;
|
||||
|
||||
background-color: $bg-color;
|
||||
|
||||
margin: 2px;
|
||||
margin-right: 2%;
|
||||
padding: 1px;
|
||||
border-radius: 50%;
|
||||
border: 0px;
|
||||
|
||||
@include center;
|
||||
|
||||
.addIcon {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.5);
|
||||
}
|
||||
|
||||
.light-mode & * {
|
||||
stroke: $sky3;
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
stroke: $sky0;
|
||||
}
|
||||
}
|
||||
|
||||
.subTaskInput {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
.subTaskNameInput {
|
||||
font-family: $secondary-font;
|
||||
height: 1.5em;
|
||||
width: 90%;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
padding: 5px 10px;
|
||||
|
||||
.light-mode & {
|
||||
background-color: $blue0;
|
||||
color: $independence-gray0;
|
||||
&::placeholder {
|
||||
color: $independence-gray1;
|
||||
}
|
||||
}
|
||||
.dark-mode & {
|
||||
background-color: $eerie-black0;
|
||||
color: $dark-primary;
|
||||
&::placeholder {
|
||||
color: $dark-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.warnTitle {
|
||||
&::placeholder {
|
||||
color: $unchecked-color !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -11,7 +11,7 @@
|
||||
:checked="subTask.checked"
|
||||
/>
|
||||
</ul>
|
||||
<button @click="addSubTask(parent)">Add subtask</button>
|
||||
<SubTaskInput :parent="parent" />
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
@@ -20,11 +20,6 @@
|
||||
import Vue from 'vue';
|
||||
import sm from '~/libs/storageManagement';
|
||||
|
||||
const subTaskItem: SubTask = {
|
||||
description: 'Task',
|
||||
checked: false,
|
||||
};
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'SubTasks',
|
||||
props: {
|
||||
@@ -55,23 +50,6 @@ export default Vue.extend({
|
||||
this.preventAnim = false;
|
||||
}, 1000);
|
||||
},
|
||||
methods: {
|
||||
addSubTask: (parentId: number) => {
|
||||
if (sm.get('tasks') === undefined) sm.add('tasks', []);
|
||||
|
||||
const localTasks: Task[] = sm.get('tasks');
|
||||
const parentTask = localTasks[parentId];
|
||||
|
||||
if (!parentTask.subTasks) parentTask.subTasks = [];
|
||||
|
||||
subTaskItem.description = `Description sub task ${parentTask.subTasks.length}`;
|
||||
|
||||
parentTask.subTasks.push(subTaskItem);
|
||||
localTasks[parentId] = parentTask;
|
||||
|
||||
sm.set('tasks', localTasks);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -337,6 +337,7 @@ export default Vue.extend({
|
||||
|
||||
.info {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
@@ -347,6 +348,7 @@ export default Vue.extend({
|
||||
font-family: $secondary-font;
|
||||
font-size: 0.8em;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
329
src/components/Task/TaskInput.vue
Normal file
329
src/components/Task/TaskInput.vue
Normal file
@@ -0,0 +1,329 @@
|
||||
<template>
|
||||
<div class="alignInputContainer">
|
||||
<div class="taskInputContainer">
|
||||
<button class="addTask" @click="addTask()">
|
||||
<IconAdd class="addIcon" />
|
||||
</button>
|
||||
<form class="taskInputs">
|
||||
<div class="taskNameInputContainer">
|
||||
<input
|
||||
id="newTaskInputName"
|
||||
class="taskNameInput"
|
||||
type="text"
|
||||
name="TaskNameInput"
|
||||
placeholder="Task Title..."
|
||||
maxlength="28"
|
||||
@keypress.enter="addTask()"
|
||||
/>
|
||||
</div>
|
||||
<div class="taskDescriptionInputContainer">
|
||||
<textarea
|
||||
id="newTaskInputDescription"
|
||||
class="taskDescriptionInput"
|
||||
name="TaskDescriptionInput"
|
||||
placeholder="Task Description..."
|
||||
maxlength="150"
|
||||
></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import sm from '~/libs/storageManagement';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'TaskInput',
|
||||
methods: {
|
||||
addTask: () => {
|
||||
if (sm.get('tasks') === undefined) sm.add('tasks', []);
|
||||
|
||||
const titleInput = document.querySelector(
|
||||
'input#newTaskInputName'
|
||||
) as HTMLInputElement;
|
||||
|
||||
const newTitle = titleInput.value;
|
||||
|
||||
const newDescription = (
|
||||
document.querySelector(
|
||||
'textarea#newTaskInputDescription'
|
||||
) as HTMLTextAreaElement
|
||||
).value;
|
||||
|
||||
if (!newTitle || newTitle === '') {
|
||||
if (!titleInput.className.includes(' warnTitle'))
|
||||
titleInput.className += ' warnTitle';
|
||||
|
||||
(
|
||||
document.querySelector('input#newTaskInputName') as HTMLInputElement
|
||||
).className = titleInput.className;
|
||||
|
||||
setTimeout(() => {
|
||||
(
|
||||
document.querySelector('input#newTaskInputName') as HTMLInputElement
|
||||
).className = titleInput.className.replace(' warnTitle', '');
|
||||
}, 1500);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const taskItem: Task = {
|
||||
title: newTitle,
|
||||
description: newDescription || '',
|
||||
checked: false,
|
||||
newTask: true,
|
||||
autoCheck: true,
|
||||
subTasks: [],
|
||||
};
|
||||
|
||||
const localTasks: Task[] = sm.get('tasks');
|
||||
|
||||
localTasks.push(taskItem);
|
||||
|
||||
sm.set('tasks', localTasks);
|
||||
|
||||
(
|
||||
document.querySelector('input#newTaskInputName') as HTMLInputElement
|
||||
).value = '';
|
||||
|
||||
(
|
||||
document.querySelector(
|
||||
'textarea#newTaskInputDescription'
|
||||
) as HTMLInputElement
|
||||
).value = '';
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~assets/styles/_variables.scss';
|
||||
@import '~assets/styles/_mixins.scss';
|
||||
|
||||
.alignInputContainer {
|
||||
@include center;
|
||||
@media not screen and (pointer: none), (pointer: coarse) {
|
||||
&:not(:hover),
|
||||
.taskInputContainer *:not(:focus-within) {
|
||||
.taskInputContainer {
|
||||
animation: shrink 0.5s forwards;
|
||||
@keyframes shrink {
|
||||
from {
|
||||
padding: 5px 10px;
|
||||
margin: 10px 0;
|
||||
transform: scale(100%);
|
||||
width: 100%;
|
||||
}
|
||||
to {
|
||||
padding: 0px 25%;
|
||||
margin: 0px 0;
|
||||
transform: scale(60%);
|
||||
width: 0%;
|
||||
}
|
||||
}
|
||||
.taskInputs {
|
||||
animation: hideInputs 0.5s forwards;
|
||||
@keyframes hideInputs {
|
||||
from {
|
||||
transform: translateX(0%);
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
}
|
||||
to {
|
||||
transform: translateX(25%);
|
||||
opacity: 0;
|
||||
width: 0%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.taskInputContainer {
|
||||
animation: expand 0.5s forwards;
|
||||
@keyframes expand {
|
||||
from {
|
||||
padding: 0px 25%;
|
||||
margin: 0px 0;
|
||||
transform: scale(60%);
|
||||
width: 0%;
|
||||
}
|
||||
to {
|
||||
padding: 5px 10px;
|
||||
margin: 10px 0;
|
||||
transform: scale(100%);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.taskInputs {
|
||||
animation: showInputs 0.5s forwards;
|
||||
@keyframes showInputs {
|
||||
from {
|
||||
transform: translateX(25%);
|
||||
opacity: 0;
|
||||
width: 0%;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0%);
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.taskInputContainer {
|
||||
&,
|
||||
* {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
*::placeholder {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.light-mode & {
|
||||
background-color: $blue0;
|
||||
color: $independence-gray0;
|
||||
}
|
||||
.dark-mode & {
|
||||
background-color: $eerie-black0;
|
||||
color: $dark-primary;
|
||||
}
|
||||
|
||||
border-radius: 20px;
|
||||
@include center;
|
||||
|
||||
@media (pointer: none), (pointer: coarse) {
|
||||
padding: 5px 10px;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.addTask {
|
||||
$bg-color: #00000080;
|
||||
|
||||
background-color: $bg-color;
|
||||
|
||||
margin: 2px;
|
||||
margin-right: 2%;
|
||||
padding: 1px;
|
||||
border-radius: 50%;
|
||||
border: 0px;
|
||||
|
||||
@include center;
|
||||
|
||||
.addIcon {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.5);
|
||||
}
|
||||
|
||||
.light-mode & * {
|
||||
stroke: $sky3;
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
stroke: $sky0;
|
||||
}
|
||||
}
|
||||
|
||||
.taskInputs {
|
||||
text-align: left;
|
||||
|
||||
.taskNameInputContainer,
|
||||
.taskDescriptionInputContainer {
|
||||
width: 100%;
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 90%;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.light-mode & {
|
||||
color: $independence-gray0;
|
||||
&::placeholder {
|
||||
color: $independence-gray1;
|
||||
}
|
||||
}
|
||||
.dark-mode & {
|
||||
color: $dark-primary;
|
||||
&::placeholder {
|
||||
color: $dark-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
background-color: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
padding: 5px 10px;
|
||||
.light-mode & {
|
||||
background-color: $blue2;
|
||||
&::placeholder {
|
||||
color: $independence-gray0;
|
||||
}
|
||||
}
|
||||
.dark-mode & {
|
||||
background-color: $dark-background;
|
||||
&::placeholder {
|
||||
color: $dark-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.taskNameInputContainer {
|
||||
.taskNameInput {
|
||||
font-family: $main-font;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
& .taskNameInput:focus,
|
||||
& .taskNameInput:hover {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.warnTitle {
|
||||
&::placeholder {
|
||||
color: $unchecked-color !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.taskDescriptionInputContainer {
|
||||
.taskDescriptionInput {
|
||||
overflow: auto;
|
||||
|
||||
white-space: pre-wrap;
|
||||
|
||||
font-family: $secondary-font;
|
||||
font-size: 0.85em;
|
||||
|
||||
&:focus {
|
||||
height: 5em;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
&:not(:focus) {
|
||||
overflow: hidden;
|
||||
height: 1em !important;
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
& .taskDescriptionInput:focus,
|
||||
& .taskDescriptionInput:hover {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -12,7 +12,6 @@
|
||||
/>
|
||||
</ul>
|
||||
<ProgressBar />
|
||||
<button @click="addTask()">Add tasks</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,15 +19,6 @@
|
||||
import Vue from 'vue';
|
||||
import sm from '~/libs/storageManagement';
|
||||
|
||||
const taskItem: Task = {
|
||||
title: 'Task 00',
|
||||
description: 'Task',
|
||||
checked: false,
|
||||
newTask: true,
|
||||
autoCheck: true,
|
||||
subTasks: [],
|
||||
};
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
@@ -49,19 +39,6 @@ export default Vue.extend({
|
||||
this.list = sm.get('tasks') ? sm.get('tasks') : [];
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
addTask: () => {
|
||||
if (sm.get('tasks') === undefined) sm.add('tasks', []);
|
||||
|
||||
const localTasks: Task[] = sm.get('tasks');
|
||||
taskItem.title = `Title Task ${localTasks.length}`;
|
||||
taskItem.description = `Description Task ${localTasks.length}`;
|
||||
|
||||
localTasks.push(taskItem);
|
||||
|
||||
sm.set('tasks', localTasks);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
30
src/components/icon/Add.vue
Normal file
30
src/components/icon/Add.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
stroke-width="1.5"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8 12H12M16 12H12M12 12V8M12 12V16"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
name: 'IconAdd',
|
||||
});
|
||||
</script>
|
||||
@@ -7,6 +7,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<TaskList />
|
||||
<TaskInput />
|
||||
</div>
|
||||
<PageFooter />
|
||||
</main>
|
||||
|
||||
4
src/static/icons/add-icon.svg
Normal file
4
src/static/icons/add-icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 12H12M16 12H12M12 12V8M12 12V16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 433 B |
Reference in New Issue
Block a user