Commit 04ded918 authored by GuiΩ's avatar GuiΩ
Browse files

feat(QuestionList): multiple questions can be move together

parent 28c86411
......@@ -38,7 +38,7 @@
#question-selector
theme-tree#theme-selector(
:selection_ids="selection_theme_id"
@selection-changed="setCurrentTheme($event[0])")/
@selection-changed="$store.dispatch('themes/setCurrentTheme', $event[0])")/
#question-column
button(data-testid="newQuestion" @click="$refs.questionEditor.edit()") Nouvelle question
#question-list-container
......
......@@ -18,10 +18,10 @@ Events:
drop-context-menu(ref="rightContextMenu")
div(
v-if="editable(questionSel)"
@click="$emit('question-edit', {question_id: questionSel.id}); $refs.rightContextMenu.close()"
v-if="editable(questionSel[0])"
@click="$emit('question-edit', {question_id: questionSel[0].id}); $refs.rightContextMenu.close()"
) Modifier
div(@click="$emit('question-duplicate', {question_id: questionSel.id}); $refs.rightContextMenu.close()") Dupliquer
div(@click="$emit('question-duplicate', {question_id: questionSel[0].id}); $refs.rightContextMenu.close()") Dupliquer
div(@click="$refs.rightContextMenu.close()") Annuler
div
......@@ -31,12 +31,15 @@ Events:
ref="questions"
:index="index"
:question_id="question_id"
@hideDrop="hideDrop"
@showDrop="showDrop"
@drop-context-menu="dropContextMenu"
:selected="questionSel.indexOf(question_id)>=0"
@click="onQuestionClick($event)"
@dblclick="onQuestionDblClick($event)"
@dragStart="onDragStart($event)"
@hideDrop="onHideDrop"
@showDrop="onShowDrop"
@drop-context-menu="onDropContextMenu"
@question-drop="$emit('question-drop', $event)"
@question-edit="$emit('question-edit', $event)"
@show-question-tooltip="$emit('show-question-tooltip', $event)")/
@question-edit="$emit('question-edit', $event)")/
</template>
<script>
......@@ -59,7 +62,7 @@ export default {
scores: {},
dropEvent: null,
dropIndex: 0,
questionSel: null,
questionSel: [],
}
},
computed: {
......@@ -69,54 +72,118 @@ export default {
question_ids() { // list des questions à afficher
return this.$store.getters["questions/currQuestionsIds"]
},
selLength() {
return this.questionSel.length;
}
},
methods: {
isSelected(question_id) {
return question_id in this.questionSel;
},
editable(question) {
return (question && question.editable) || permissions.includes('Validateur')
},
dropContextMenu(event) {
/**
* init dataTransfer for a drag
*/
onDragStart(event) {
let question = event.question
if (this.questionSel.indexOf(question.id) < 0)
this.questionSel = [question.id];
if (question.avalider || permissions.includes('Validateur'))
event.dataTransfer.effectAllowed = 'copyMove';
else if (!question.avalider)
event.dataTransfer.effectAllowed = 'move';
else
event.dataTransfer.effectAllowed = 'copy';
event.dataTransfer.setData('question_id', question.id);
event.dataTransfer.setData('index', event.index);
},
onDropContextMenu(event) {
this.dropEvent = event.dropEvent;
this.dropIndex = event.dropIndex;
this.$refs.dropContextMenu.show(event.dropEvent)
},
showDrop() {
onShowDrop() {
this.$el.style.background = '#f99';
},
hideDrop() {
onHideDrop() {
this.$el.style.background = '';
},
onQuestionClick(event) {
if (event.shiftKey && permissions.includes('Validateur')) {
if (this.selLength) {
let first = this.$store.getters['questions/indexInTheme'](this.questionSel[this.questionSel.length -1], this.theme_id);
let last = this.$store.getters['questions/indexInTheme'](event.question_id, this.theme_id);
let inc = 1;
if (last < first)
inc = -1;
for (let i = first+inc; i*inc <= last*inc ; i+= inc) {
let question_id = this.$refs.questions[i].question.id;
if (this.questionSel.indexOf(question_id) < 0)
this .questionSel.push(question_id);
}
}
} else if (event.ctrlKey && permissions.includes('Validateur')) {
let index = this.questionSel.indexOf(event.question_id);
if (index >= 0) {
this.questionSel.splice(index, 1);
} else {
this.questionSel.push(event.question_id);
}
} else
this.questionSel = [event.question_id];
if ((! event.ctrlKey && ! event.shiftKey) || !permissions.includes('Validateur') )
this.$emit('show-question-tooltip', event)
},
onQuestionDblClick(event) {
this.questionSel = [event.question_id];
},
changePosition() {
const indexSource = this.dropEvent.dataTransfer.getData('index');
const positionDest = this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id);
this.$el.style.cursor = 'wait'
let newPosition;
let newPosition = []
// on unordered question => after last unodered
// on unordered question => beforse first unodered
if (positionDest === null) {
const firstOrdered = this.findFirstOrdered();
if (firstOrdered) {
newPosition = firstOrdered.question.getPositionInTheme(this.theme_id) - 128;
for (let i = this.selLength-1; i >= 0; i--) {
newPosition[i] = firstOrdered.question.getPositionInTheme(this.theme_id) - (i+1)*128;
}
} else {
newPosition = 0
for (let i = 0; i< this.selLength; i++) {
newPosition[i] = i*128;
}
}
} else if (this.dropIndex < indexSource) { // before the actual position
if (this.dropIndex === 0 || !this.$refs.questions[this.dropIndex - 1].question.getPositionInTheme(this.theme_id))
// before the first ordored question
newPosition = this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id) - 128;
else
newPosition = (this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id)
+ this.$store.getters['questions/question'](this.question_ids[this.dropIndex - 1])
.getPositionInTheme(this.theme_id))/ 2;
for (let i = this.selLength-1; i >= 0; i--) {
newPosition[i] = this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id) - (i+1)*128;
}
else {
let gap = this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id)
- this.$store.getters['questions/question'](this.question_ids[this.dropIndex - 1]).getPositionInTheme(this.theme_id);
for (let i = 0; i< this.selLength; i++) {
newPosition[i] = this.$refs.questions[this.dropIndex-1].question.getPositionInTheme(this.theme_id) + (i+1) *gap/(this.selLength + 1)
}
}
} else if (this.dropIndex > indexSource) { // after the actual position
if (this.dropIndex === this.question_ids.length - 1) {
newPosition = this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id) + 128 // after the last question
for (let i = 0; i< this.selLength; i++) {
newPosition[i] = this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id) + 128*(i+1) // after the last question
}
} else {
console.log(this.$refs.questions[this.dropIndex].question.id
, this.$store.getters['questions/question'](this.question_ids[this.dropIndex + 1]).getPositionInTheme(this.theme_id))
newPosition = (this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id)
+ this.$store.getters['questions/question'](this.question_ids[this.dropIndex + 1]).getPositionInTheme(this.theme_id)) / 2
let gap = this.$store.getters['questions/question'](this.question_ids[this.dropIndex + 1]).getPositionInTheme(this.theme_id)
- this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id);
for (let i = 0; i< this.selLength; i++) {
newPosition[i] = this.$refs.questions[this.dropIndex].question.getPositionInTheme(this.theme_id) + (i+1) *gap/(this.selLength + 1)
}
}
} else { // on itself (probably a bug)
consol.warn('drop on itself')
......@@ -124,12 +191,24 @@ export default {
this.endDrop()
return;
}
this.$store.dispatch('questions/changeQuestionPosition', {
question_id: this.dropEvent.dataTransfer.getData('question_id'),
newValues: {theme_id: this.theme_id, position: newPosition}
})
.then(() => this.$el.style.cursor = '')
.catch(() => this.$el.style.cursor = '')
this.questionSel.sort((a,b) => {
let posa = this.$store.getters['questions/question'](a).getPositionInTheme(this.theme_id);
let posb = this.$store.getters['questions/question'](b).getPositionInTheme(this.theme_id);
if (posa < posb)
return -1;
else if (posb < posa)
return 1;
else
return 0;
});
let promises = []
for (let i = 0; i < this.selLength; i++) {
promises.push(this.$store.dispatch('questions/changeQuestionPosition', {
question_id: this.questionSel[i],
newValues: {theme_id: this.theme_id, position: newPosition[i]}
}));
}
Promise.all(promises).then(() => this.$el.style.cursor = '');
this.endDrop()
},
endDrop() {
......@@ -169,7 +248,7 @@ export default {
return this.$refs.questions[index]
},
showContextMenu(event, question) {
this.questionSel = question;
this.questionSel = [question.id];
this.$refs.rightContextMenu.show(event);
}
},
......
<template lang="pug">
.question(:data-testid="'question_' + question_id")
.question(:data-testid="'question_' + question_id" :class="{selected:selected}")
score(
v-for="(score, index) in scores"
:key="index"
......@@ -14,8 +14,8 @@
:position="position"
:src="('../static/thumbs/' + question.thumb_name)"
draggable
@click="click($event, question_id)"
@dblclick="questionEdit(question)"
@click="onClick($event, question_id)"
@dblclick="onDblClick(question_id)"
@dragenter.prevent="onDragOver($event, index)"
@dragover.prevent
@dragleave.prevent="onDragLeave($event, index)"
......@@ -36,6 +36,7 @@ export default {
props: {
question_id: Number,
index: Number,
selected: Boolean
},
computed: {
question() {
......@@ -54,35 +55,33 @@ export default {
data() {
return {
nClick: 0,
clickTimeout: 300,
clickTimeout: 200,
clickTimer: null,
}
},
methods: {
click(event, question_id) {
onClick(event, question_id) {
this.nClick++
if (this.nClick === 1) {
this.clickTimer = setTimeout( () => {
this.$emit('show-question-tooltip', {event: event, question_id: question_id})
this.clickTimer = setTimeout(() => {
event.question_id = question_id
this.$emit('click', event)
this.nClick = 0
}, this.clickTimeout);
} else {
clearTimeout(this.clickTimer);
this.nClick = 0;
}
},
onDblClick(event, question_id){
clearTimeout(this.clickTimer);
this.nClick = 0;
this.$emit('dblclick', event)
},
/**
* init dataTransfer for a drag
*/
dragStart(event, index, question) {
if (question.avalider || permissions.includes('Validateur'))
event.dataTransfer.effectAllowed = 'copyMove';
else if (!question.avalider)
event.dataTransfer.effectAllowed = 'move';
else
event.dataTransfer.effectAllowed = 'copy';
event.dataTransfer.setData('question_id', question.id);
event.dataTransfer.setData('index', index);
event.question = question;
event.index = index;
this.$emit('dragStart', event);
},
onDragOver(dragEvent, indexDest) {
const questionPoseeSource_id = dragEvent.dataTransfer.getData('questionPosee_id')
......@@ -154,6 +153,9 @@ export default {
}
},
questionEdit(question) {
clearTimeout(this.clickTimer);
this.nClick = 0;
this.$emit('d', {question_id: question.id})
if (question.editable || permissions.includes('Validateur'))
this.$emit('question-edit', {question_id: question.id})
},
......@@ -169,6 +171,9 @@ export default {
padding: 0;
margin: 0;
}
.question.selected {
background-color: rgba(255, 150, 150, .8);
}
img.editable {
background: rgba(100, 100, 100, .8);
......
......@@ -39,6 +39,13 @@ const getters = {
questionSaveStatus: (state) => {
return state.questionSaveStatus.status
},
indexInTheme: (state, getters, rootState) => (question_id, theme_id) => {
for (let i = 0; i < rootState.themes.themes[theme_id].questions.length; i++) {
if ( rootState.themes.themes[theme_id].questions[i] === question_id)
return i;
}
return -1;
}
}
// mutations
......@@ -175,4 +182,4 @@ function getQuestionPositionInTheme(theme_id) {
}
}
return null;
}
\ No newline at end of file
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment