Browse Source

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>
main
konrad 2 months ago
parent
commit
442e6b12e0
  1. 4
      cypress/integration/task/task.spec.js
  2. 52
      src/components/tasks/add-task.vue

4
cypress/integration/task/task.spec.js

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

52
src/components/tasks/add-task.vue

@ -2,16 +2,17 @@
<div class="task-add">
<div class="field is-grouped">
<p class="control has-icons-left is-expanded">
<input
<textarea
:disabled="taskService.loading || null"
@keyup.enter="addTask()"
class="input"
:placeholder="$t('list.list.addPlaceholder')"
type="text"
v-focus
v-model="newTaskTitle"
ref="newTaskInput"
:style="{'height': `${textAreaHeight}px`}"
@keyup="errorMessage = ''"
@keydown.enter="handleEnter"
/>
<span class="icon is-small is-left">
<icon icon="tasks"/>
@ -40,6 +41,12 @@ import TaskService from '../../services/task'
import createTask from '@/components/tasks/mixins/createTask'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
const INITIAL_SCROLL_HEIGHT = 40
const cleanupTitle = title => {
return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '')
}
export default {
name: 'add-task',
data() {
@ -47,6 +54,7 @@ export default {
newTaskTitle: '',
taskService: new TaskService(),
errorMessage: '',
textAreaHeight: INITIAL_SCROLL_HEIGHT,
}
},
mixins: [
@ -61,6 +69,16 @@ export default {
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: {
addTask() {
if (this.newTaskTitle === '') {
@ -73,10 +91,20 @@ export default {
return
}
this.createNewTask(this.newTaskTitle, 0, this.$store.state.auth.settings.defaultListId, this.defaultPosition)
.then(task => {
const newTasks = []
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.$emit('taskAdded', task)
})
.catch(e => {
if (e === 'NO_LIST') {
@ -86,6 +114,16 @@ export default {
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>
@ -98,4 +136,8 @@ export default {
height: 2.5rem;
}
}
.input, .textarea {
transition: border-color $transition;
}
</style>

Loading…
Cancel
Save