feat: allow quickly creating multiple tasks at once with multiline input (#796)

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#796
Co-authored-by: konrad <k@knt.li>
Co-committed-by: konrad <k@knt.li>
This commit is contained in:
konrad 2021-09-26 18:22:28 +00:00
parent 3dfa286a12
commit 442e6b12e0
2 changed files with 49 additions and 7 deletions

View File

@ -27,7 +27,7 @@ describe('Task', () => {
it('Should be created new', () => { it('Should be created new', () => {
cy.visit('/lists/1/list') cy.visit('/lists/1/list')
cy.get('input.input[placeholder="Add a new task…"') cy.get('.input[placeholder="Add a new task…"')
.type('New Task') .type('New Task')
cy.get('.button') cy.get('.button')
.contains('Add') .contains('Add')
@ -43,7 +43,7 @@ describe('Task', () => {
cy.visit('/lists/1/list') cy.visit('/lists/1/list')
cy.get('.list-is-empty-notice') cy.get('.list-is-empty-notice')
.should('not.exist') .should('not.exist')
cy.get('input.input[placeholder="Add a new task…"') cy.get('.input[placeholder="Add a new task…"')
.type('New Task') .type('New Task')
cy.get('.button') cy.get('.button')
.contains('Add') .contains('Add')

View File

@ -2,16 +2,17 @@
<div class="task-add"> <div class="task-add">
<div class="field is-grouped"> <div class="field is-grouped">
<p class="control has-icons-left is-expanded"> <p class="control has-icons-left is-expanded">
<input <textarea
:disabled="taskService.loading || null" :disabled="taskService.loading || null"
@keyup.enter="addTask()"
class="input" class="input"
:placeholder="$t('list.list.addPlaceholder')" :placeholder="$t('list.list.addPlaceholder')"
type="text" type="text"
v-focus v-focus
v-model="newTaskTitle" v-model="newTaskTitle"
ref="newTaskInput" ref="newTaskInput"
:style="{'height': `${textAreaHeight}px`}"
@keyup="errorMessage = ''" @keyup="errorMessage = ''"
@keydown.enter="handleEnter"
/> />
<span class="icon is-small is-left"> <span class="icon is-small is-left">
<icon icon="tasks"/> <icon icon="tasks"/>
@ -40,6 +41,12 @@ import TaskService from '../../services/task'
import createTask from '@/components/tasks/mixins/createTask' import createTask from '@/components/tasks/mixins/createTask'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue' import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
const INITIAL_SCROLL_HEIGHT = 40
const cleanupTitle = title => {
return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '')
}
export default { export default {
name: 'add-task', name: 'add-task',
data() { data() {
@ -47,6 +54,7 @@ export default {
newTaskTitle: '', newTaskTitle: '',
taskService: new TaskService(), taskService: new TaskService(),
errorMessage: '', errorMessage: '',
textAreaHeight: INITIAL_SCROLL_HEIGHT,
} }
}, },
mixins: [ mixins: [
@ -61,6 +69,16 @@ export default {
required: false, required: false,
}, },
}, },
watch: {
newTaskTitle(newVal) {
let scrollHeight = this.$refs.newTaskInput.scrollHeight
if (scrollHeight < INITIAL_SCROLL_HEIGHT || newVal === '') {
scrollHeight = INITIAL_SCROLL_HEIGHT
}
this.textAreaHeight = scrollHeight
},
},
methods: { methods: {
addTask() { addTask() {
if (this.newTaskTitle === '') { if (this.newTaskTitle === '') {
@ -73,10 +91,20 @@ export default {
return return
} }
this.createNewTask(this.newTaskTitle, 0, this.$store.state.auth.settings.defaultListId, this.defaultPosition) const newTasks = []
.then(task => { this.newTaskTitle.split(/[\r\n]+/).forEach(t => {
newTasks.push(
this.createNewTask(cleanupTitle(t), 0, this.$store.state.auth.settings.defaultListId, this.defaultPosition)
.then(task => {
this.$emit('taskAdded', task)
return task
}),
)
})
Promise.all(newTasks)
.then(() => {
this.newTaskTitle = '' this.newTaskTitle = ''
this.$emit('taskAdded', task)
}) })
.catch(e => { .catch(e => {
if (e === 'NO_LIST') { if (e === 'NO_LIST') {
@ -86,6 +114,16 @@ export default {
this.$message.error(e) this.$message.error(e)
}) })
}, },
handleEnter(e) {
// when pressing shift + enter we want to continue as we normally would. Otherwise, we want to create
// the new task(s). The vue event modifier don't allow this, hence this method.
if (e.shiftKey) {
return
}
e.preventDefault()
this.addTask()
},
}, },
} }
</script> </script>
@ -98,4 +136,8 @@ export default {
height: 2.5rem; height: 2.5rem;
} }
} }
.input, .textarea {
transition: border-color $transition;
}
</style> </style>