Skip to content
Snippets Groups Projects
Commit 7f2da668 authored by Jan-Hendrik Willms's avatar Jan-Hendrik Willms
Browse files

fixes #3161

Closes #3161

Merge request studip/studip!2139
parent 851597a8
No related branches found
No related tags found
No related merge requests found
......@@ -10,8 +10,10 @@ $questiontypes['Vote'] = [
'component' => Vote::getEditingComponent()
];
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'QuestionType')) {
if (!isset($questiontypes[$class])) {
if (
is_subclass_of($class, QuestionType::class)
&& !isset($questiontypes[$class])
) {
$questiontypes[$class] = [
'name' => $class::getName(),
'type' => $class,
......@@ -20,9 +22,8 @@ foreach (get_declared_classes() as $class) {
];
}
}
}
$questionnaire_data = [
'id' => $questionnaire->getId(),
'id' => $questionnaire->id,
'title' => $questionnaire['title'],
'startdate' => $questionnaire->isNew() ? _('sofort') : $questionnaire['startdate'],
'stopdate' => $questionnaire['stopdate'],
......@@ -34,14 +35,14 @@ $questionnaire_data = [
$questions_data = [];
foreach ($questionnaire->questions as $question) {
$questions_data[] = [
'id' => $question->getId(),
'id' => $question->id,
'questiontype' => $question['questiontype'],
'internal_name' => $question['internal_name'],
'questiondata' => $question['questiondata']->getArrayCopy()
];
}
?>
<form action="<?= URLHelper::getLink("dispatch.php/questionnaire/edit/".(!$questionnaire->isNew() ? $questionnaire->getId() : "")) ?>"
<form action="<?= URLHelper::getLink('dispatch.php/questionnaire/edit/' . (!$questionnaire->isNew() ? $questionnaire->getId() : '')) ?>"
method="post"
enctype="multipart/form-data"
class="questionnaire_edit default"
......@@ -50,19 +51,17 @@ foreach ($questionnaire->questions as $question) {
data-questions_data="<?= htmlReady(json_encode($questions_data)) ?>"
data-range_type="<?= htmlReady(Request::get('range_type')) ?>"
data-range_id="<?= htmlReady(Request::get('range_id')) ?>"
<?= Request::isAjax() ? "data-dialog" : "" ?>
<?= Request::isAjax() ? 'data-dialog' : '' ?>
:data-secure="activateFormSecure">
<div class="editor">
<div class="rightside" aria-live="polite" tabindex="0" ref="rightside">
<div class="admin" v-if="activeTab === 'admin'">
<article aria-live="assertive"
class="validation_notes studip">
<article aria-live="assertive" class="validation_notes studip">
<header>
<h1>
<?= Icon::create('info-circle', Icon::ROLE_INFO)->asImg(17, ['class' => "text-bottom validation_notes_icon"]) ?>
<?= Icon::create('info-circle', Icon::ROLE_INFO)->asImg(['class' => 'text-bottom validation_notes_icon']) ?>
<?= _('Hinweise zum Ausfüllen des Formulars') ?>
</h1>
</header>
......@@ -128,26 +127,25 @@ foreach ($questionnaire->questions as $question) {
:ref="key == Object.keys(questiontypes)[0] ? 'autofocus' : ''"
href=""
@click.prevent="addQuestion(questiontype.type)">
<studip-icon :shape="questiontype.icon" :size="40" role="clickable"></studip-icon>
<studip-icon :shape="questiontype.icon" :size="40"></studip-icon>
{{questiontype.name}}
</button>
</div>
</div>
<div v-else>
<? foreach ($questiontypes as $questiontype) : ?>
<component is="<?= htmlReady($questiontype['component'][0]) ?>"
v-if="questiontypes[questions[getIndexForQuestion(activeTab)].questiontype].component[0] === '<?= htmlReady($questiontype['component'][0]) ?>'"
v-model="questions[getIndexForQuestion(activeTab)].questiondata"
:question_id="questions[getIndexForQuestion(activeTab)].id">
<component :is="questiontypes[questions[indexForQuestion].questiontype].component[0]"
v-model="questions[indexForQuestion].questiondata"
:question_id="questions[indexForQuestion].id"
:key="questions[indexForQuestion].id">
</component>
<? endforeach ?>
</div>
</div>
<aside>
<a :class="activeTab === 'admin' ? 'admin active' : 'admin'"
href=""
<a class="admin"
:class="{active: activeTab === 'admin'}"
href="#"
@click.prevent="switchTab('admin')">
<span class="icon"><studip-icon shape="evaluation" role="clickable" :size="30" alt=""></studip-icon></span>
<span class="icon"><studip-icon shape="evaluation" :size="30" alt=""></studip-icon></span>
<?= _('Einstellungen') ?>
</a>
<draggable v-if="questions.length > 0" v-model="questions" handle=".drag-handle" group="questions" class="questions_container questions">
......@@ -160,17 +158,17 @@ foreach ($questionnaire->questions as $question) {
@click.prevent="switchTab(question.id)">
<span class="drag-handle"></span>
<span class="icon type">
<studip-icon :shape="questiontypes[question.questiontype].icon" role="clickable" :size="30" alt=""></studip-icon>
<studip-icon :shape="questiontypes[question.questiontype].icon" :size="30" alt=""></studip-icon>
</span>
<div v-if="editInternalName !== question.id">{{ question.internal_name || questiontypes[question.questiontype].name}}</div>
<div v-else class="inline_editing">
<input type="text" ref="editInternalName" v-model="tempInternalName" class="inlineediting_internal_name">
<button @click="saveInternalName(question.id)">
<studip-icon shape="accept" role="clickable" :size="20" title="<?= _('Internen Namen speichern') ?>"></studip-icon>
<studip-icon shape="accept" :size="20" title="<?= _('Internen Namen speichern') ?>"></studip-icon>
</button>
<button @click="editInternalName = null">
<studip-icon shape="decline" role="clickable" :size="20" title="<?= _('Internen Namen nicht speichern') ?>"></studip-icon>
<studip-icon shape="decline" :size="20" title="<?= _('Internen Namen nicht speichern') ?>"></studip-icon>
</button>
</div>
</a>
......@@ -180,33 +178,20 @@ foreach ($questionnaire->questions as $question) {
@rename="renameInternalName(question.id)"
@moveup="moveQuestionUp(question.id)"
@movedown="moveQuestionDown(question.id)"
@delete="askForDeletingTheQuestion(question.id)"></studip-action-menu>
@delete="deleteQuestion(question.id)"></studip-action-menu>
</div>
</draggable>
<a :class="activeTab === 'add_question' ? 'add_question active' : 'add_question'"
href=""
href="#"
@click.prevent="switchTab('add_question')">
<span class="icon"><studip-icon shape="add" role="clickable" :size="30" alt=""></studip-icon></span>
<span class="icon"><studip-icon shape="add" :size="30" alt=""></studip-icon></span>
<?= _('Element hinzufügen') ?>
</a>
</aside>
</div>
<studip-dialog
v-if="askForDeletingQuestions"
title="<?= _('Bitte bestätigen Sie die Aktion') ?>"
question="<?= _('Wirklich löschen?') ?>"
confirmText="<?= _('Ja') ?>"
closeText="<?= _('Nein') ?>"
closeClass="cancel"
height="180"
@confirm="deleteQuestion"
@close="askForDeletingQuestions = false"
>
</studip-dialog>
<footer data-dialog-button>
<?= \Studip\LinkButton::create(_("Speichern"), 'questionnaire_store', ['onclick' => 'STUDIP.Questionnaire.Editor.submit(); return false;']) ?>
<?= Studip\LinkButton::create(_('Speichern'), 'questionnaire_store', ['onclick' => 'STUDIP.Questionnaire.Editor.submit(); return false;']) ?>
</footer>
</form>
<?php
/**
* @property string $id
* @property string $questionnaire_id
* @property string $title
* @property string $description
* @property string $user_id
* @property int|null $startdate
* @property int|null $stopdate
* @property bool $visible
* @property bool $anonymous
* @property string $resultvisibility
* @property bool $editanswers
* @property bool $copyable
* @property string $chdate
* @property string $mkdate
*
* @property QuestionnaireQuestion[]|SimpleORMapCollection $questions
* @property QuestionnaireAssignment[]|SimpleORMapCollection $assignments
* @property QuestionnaireAnonymousAnswer[]|SimpleORMapCollection $anonymousanswers
*/
class Questionnaire extends SimpleORMap implements PrivacyObject
{
protected static function configure($config = [])
......
......@@ -7,7 +7,7 @@ const Questionnaire = {
delayedQueue: [],
Editor: null,
initEditor () {
$('.questionnaire_edit').each(function () {
$('.questionnaire_edit:not(.vueified)').addClass('vueified').each(function () {
STUDIP.Vue.load().then(({createApp}) => {
let form = this;
let components = {};
......@@ -26,15 +26,15 @@ const Questionnaire = {
components['questionnaire-info-edit'] = () => import('../../../vue/components/questionnaires/QuestionnaireInfoEdit.vue');
STUDIP.Questionnaire.Editor = createApp({
el: form,
components,
data() {
return {
questiontypes,
questions: $(form).data('questions_data'),
activeTab: 'admin',
hoverTab: null,
questiontypes: questiontypes,
data: $(form).data('questionnaire_data'),
askForDeletingQuestions: false,
whatQuestionIndexShouldBeDeleted: null,
form_secured: true,
oldData: {
questions: [],
......@@ -44,21 +44,23 @@ const Questionnaire = {
range_id: $(form).data('range_id'),
editInternalName: null,
tempInternalName: '',
validationNotice: false
validationNotice: false,
};
},
methods: {
addQuestion: function (questiontype) {
addQuestion(questiontype) {
let id = md5(STUDIP.USER_ID + '_QUESTIONTYPE_' + Math.random());
this.questions.push({
id: id,
questiontype: questiontype,
internal_name: '',
questiondata: {},
});
this.activeTab = id;
},
submit: function () {
submit() {
if (!this.data.title) {
this.switchTab('admin');
this.validationNotice = true;
......@@ -82,25 +84,18 @@ const Questionnaire = {
questiondata: Object.assign({}, this.questions[i].questiondata),
});
}
let v = this;
$.ajax({
url: STUDIP.URLHelper.getURL(STUDIP.ABSOLUTE_URI_STUDIP + 'dispatch.php/questionnaire/store/' + (this.data.id || '')),
data: {
$.post(STUDIP.URLHelper.getURL('dispatch.php/questionnaire/store/' + (this.data.id || '')), {
questionnaire: data,
questions_data: questions,
range_type: this.range_type,
range_id: this.range_id
},
type: 'post',
success: function () {
v.form_secured = false;
v.$nextTick(function () {
}).done(() => {
this.form_secured = false;
this.$nextTick(() => {
location.reload();
});
},
error: function () {
window.alert('Could not save questionnaire.');
}
}).fail(() => {
STUDIP.Report.error('Could not save questionnaire.');
});
},
getIndexForQuestion: function (question_id) {
......@@ -121,19 +116,16 @@ const Questionnaire = {
});
this.activeTab = id;
},
askForDeletingTheQuestion: function (question_id) {
this.askForDeletingQuestions = true;
this.whatQuestionIndexShouldBeDeleted = this.getIndexForQuestion(question_id);
},
deleteQuestion: function () {
this.$delete(this.questions, this.whatQuestionIndexShouldBeDeleted);
deleteQuestion(question_id) {
STUDIP.Dialog.confirm(this.$gettext('Wirklich löschen?')).done(() => {
this.$delete(this.questions, this.getIndexForQuestion(question_id));
this.switchTab('add_question');
this.askForDeletingQuestions = false;
})
},
switchTab: function (tab_id) {
switchTab(tab_id) {
this.activeTab = tab_id;
this.$nextTick(function () {
if (typeof this.$refs.autofocus !== "undefined") {
if (this.$refs.autofocus !== undefined) {
if (Array.isArray(this.$refs.autofocus)) {
if (typeof this.$refs.autofocus[0] !== "undefined") {
this.$refs.autofocus[0].focus();
......@@ -144,23 +136,23 @@ const Questionnaire = {
}
});
},
objectsEqual: function (obj1, obj2) {
objectsEqual(obj1, obj2) {
return _.isEqual(obj1, obj2);
},
renameInternalName: function (question_id) {
renameInternalName(question_id) {
this.editInternalName = question_id;
let index = this.getIndexForQuestion(question_id);
this.tempInternalName = this.questions[index].internal_name;
this.$nextTick(function () {
this.$nextTick(() => {
this.$refs.editInternalName[0].focus();
});
},
saveInternalName: function (question_id) {
saveInternalName(question_id) {
let index = this.getIndexForQuestion(question_id);
this.questions[index].internal_name = this.tempInternalName;
this.editInternalName = null;
},
moveQuestionDown: function (question_id) {
moveQuestionDown(question_id) {
let index = this.getIndexForQuestion(question_id);
if (index < this.questions.length - 1) {
let question = this.questions[index];
......@@ -169,7 +161,7 @@ const Questionnaire = {
this.$forceUpdate();
}
},
moveQuestionUp: function (question_id) {
moveQuestionUp(question_id) {
let index = this.getIndexForQuestion(question_id);
if (index > 0) {
let question = this.questions[index];
......@@ -180,22 +172,33 @@ const Questionnaire = {
}
},
computed: {
activateFormSecure: function () {
activateFormSecure() {
let newData = {
'questions': this.questions,
'data': this.data
questions: this.questions,
data: this.data
};
return this.form_secured && !this.objectsEqual(this.oldData, newData);
},
indexForQuestion() {
for (let i in this.questions) {
if (
this.questions[i].id === this.activeTab ||
this.questions[i].id === this.activeTab.substring(5)
) {
return typeof i === "string" ? parseInt(i, 10) : i;
}
}
return null;
},
},
mounted: function () {
mounted() {
this.$refs.autofocus.focus();
this.oldData = {
'questions': [...this.questions],
'data': Object.assign({}, this.data)
questions: [...this.questions],
data: Object.assign({}, this.data)
};
},
components: components
});
});
......
......@@ -22,7 +22,7 @@
<td class="dragcolumn">
<a class="dragarea"
tabindex="0"
:title="$gettextInterpolate('Sortierelement für Aussage %{statement}. Drücken Sie die Tasten Pfeil-nach-oben oder Pfeil-nach-unten, um dieses Element in der Liste zu verschieben.', {statement: statement})"
:title="$gettextInterpolate($gettext('Sortierelement für Aussage %{statement}. Drücken Sie die Tasten Pfeil-nach-oben oder Pfeil-nach-unten, um dieses Element in der Liste zu verschieben.'), {statement: statement})"
@keydown="keyHandler($event, index)"
:ref="'draghandle_' + index">
<span class="drag-handle"></span>
......@@ -36,34 +36,38 @@
v-model="val_clone.statements[index]">
</td>
<td v-for="(option, index2) in val_clone.options" :key="index2">
<input type="radio" value="1" disabled :title="option">
<input type="radio" disabled :title="option">
</td>
<td class="actions">
<button class="as-link"
@click.prevent="askForDeletingStatement(index)"
:title="$gettext('Aussage löschen')">
<studip-icon shape="trash" role="clickable" :size="20" alt=""></studip-icon>
</button>
<studip-icon name="delete"
shape="trash"
:size="20"
@click.prevent="deleteStatement(index)"
:title="$gettext('Aussage löschen')"
></studip-icon>
</td>
</tr>
</draggable>
<tfoot>
<tr>
<td :colspan="typeof val_clone.options !== 'undefined' ? val_clone.options.length + 3 : 3">
<button @click.prevent="addStatement" class="as-link" :title="$gettext('Aussage hinzufügen')">
<studip-icon shape="add" role="clickable" :size="20" alt=""></studip-icon>
</button>
<td :colspan="val_clone.options.length + 3">
<studip-icon name="add"
shape="add"
:size="20"
@click.prevent="addStatement()"
:title="$gettext('Aussage hinzufügen')"
></studip-icon>
</td>
</tr>
</tfoot>
</table>
<label>
<input type="checkbox" v-model.number="val_clone.mandatory" true-value="1" false-value="0">
<input type="checkbox" v-model.number="val_clone.mandatory">
{{ $gettext('Pflichtfrage') }}
</label>
<label>
<input type="checkbox" v-model.number="val_clone.randomize" true-value="1" false-value="0">
<input type="checkbox" v-model.number="val_clone.randomize">
{{ $gettext('Antworten den Teilnehmenden zufällig präsentieren') }}
</label>
......@@ -71,39 +75,30 @@
<div>{{ $gettext('Antwortmöglichkeiten konfigurieren') }}</div>
<input-array v-model="val_clone.options"></input-array>
</div>
<studip-dialog
v-if="askForDeleting"
:title="$gettext('Bitte bestätigen Sie die Aktion.')"
:question="$gettext('Wirklich löschen?')"
:confirmText="$gettext('Ja')"
:closeText="$gettext('Nein')"
closeClass="cancel"
height="180"
@confirm="deleteStatement"
@close="askForDeleting = false"
>
</studip-dialog>
</div>
</template>
<script>
import StudipIcon from "../StudipIcon.vue";
import StudipDialog from "../StudipDialog.vue";
import draggable from 'vuedraggable';
import StudipWysiwyg from "../StudipWysiwyg.vue";
import InputArray from "./InputArray.vue";
import { $gettext } from '../../../assets/javascripts/lib/gettext.js';
const default_value = {
import { $gettext } from '../../../assets/javascripts/lib/gettext';
const default_value = () => ({
description: '',
statements: ['', '', '', ''],
options: [$gettext('trifft zu'), $gettext('trifft eher zu'), $gettext('teils-teils'), $gettext('trifft eher nicht zu'), $gettext('trifft nicht zu')]
};
mandatory: false,
randomize: false,
options: [
$gettext('trifft zu'),
$gettext('trifft eher zu'),
$gettext('teils-teils'),
$gettext('trifft eher nicht zu'),
$gettext('trifft nicht zu'),
],
});
export default {
name: 'likert-edit',
components: {
StudipWysiwyg,
StudipIcon,
StudipDialog,
draggable,
InputArray
},
......@@ -111,8 +106,8 @@ export default {
value: {
type: Object,
required: false,
default: function () {
return default_value;
default() {
return {...default_value()};
}
},
question_id: {
......@@ -120,45 +115,29 @@ export default {
required: false
}
},
data: function () {
data() {
return {
val_clone: {},
askForDeleting: false,
indexOfDeletingStatement: 0,
val_clone: null,
assistiveLive: ''
};
},
methods: {
addStatement: function (val, position) {
if (val.target) {
val = '';
}
let data = this.value;
if (typeof position === "undefined") {
data.statements.push(val || '');
position = this.value.length - 1
addStatement(val = '', position = null) {
if (position === null) {
this.val_clone.statements.push(val || '');
} else {
data.statements.splice(position, 0, val || '');
this.val_clone.statements.splice(position, 0, val || '');
}
this.$emit('input', data);
let v = this;
this.$nextTick(function () {
v.$refs['statement_' + (v.value.statements.length - 1)][0].focus();
this.$nextTick(() => {
this.$refs['statement_' + (this.val_clone.statements.length - 1)][0].focus();
});
},
askForDeletingStatement: function (index) {
this.indexOfDeletingStatement = index;
if (this.value.statements[index]) {
this.askForDeleting = true;
} else {
this.deleteStatement();
}
},
deleteStatement: function () {
this.$delete(this.value.statements, this.indexOfDeletingStatement);
this.askForDeleting = false;
deleteStatement(index) {
STUDIP.Dialog.confirm(this.$gettext('Wirklich löschen?')).done(() => {
this.$delete(this.val_clone.statements, index);
});
},
onPaste: function (ev, position) {
onPaste(ev, position) {
let data = ev.clipboardData.getData("text").split("\n");
for (let i = 0; i < data.length; i++) {
if (data[i].trim()) {
......@@ -172,11 +151,11 @@ export default {
e.preventDefault();
if (index > 0) {
this.moveUp(index);
this.$nextTick(function () {
this.$nextTick(() => {
this.$refs['draghandle_' + (index - 1)][0].focus();
this.assistiveLive = this.$gettextInterpolate(
'Aktuelle Position in der Liste: %{pos} von %{listLength}.'
, {pos: index, listLength: this.val_clone.statements.length}
this.$gettext('Aktuelle Position in der Liste: %{pos} von %{listLength}.'),
{pos: index, listLength: this.val_clone.statements.length}
);
});
}
......@@ -185,40 +164,46 @@ export default {
e.preventDefault();
if (index < this.val_clone.statements.length - 1) {
this.moveDown(index);
this.$nextTick(function () {
this.$nextTick(() => {
this.$refs['draghandle_' + (index + 1)][0].focus();
this.assistiveLive = this.$gettextInterpolate(
'Aktuelle Position in der Liste: %{pos} von %{listLength}.'
, {pos: index + 2, listLength: this.val_clone.statements.length}
this.$gettext('Aktuelle Position in der Liste: %{pos} von %{listLength}.'),
{pos: index + 2, listLength: this.val_clone.statements.length}
);
});
}
break;
}
},
moveDown: function (index) {
let statement = this.val_clone.statements[index];
this.val_clone.statements[index] = this.val_clone.statements[index + 1];
this.val_clone.statements[index + 1] = statement;
this.$forceUpdate();
moveDown(index) {
this.val_clone.statements.splice(
index,
2,
this.val_clone.statements[index + 1],
this.val_clone.statements[index]
)
},
moveUp: function (index) {
let statement = this.val_clone.statements[index];
this.val_clone.statements[index] = this.val_clone.statements[index - 1];
this.val_clone.statements[index - 1] = statement;
this.$forceUpdate();
moveUp(index) {
this.val_clone.statements.splice(
index - 1,
2,
this.val_clone.statements[index],
this.val_clone.statements[index - 1]
)
}
},
mounted: function () {
this.val_clone = this.value;
if (!this.value.statements) {
this.$emit('input', default_value);
}
created() {
this.val_clone = Object.assign({}, default_value(), this.value ?? {});
},
mounted() {
this.$refs.autofocus.focus();
},
watch: {
value (new_val) {
this.val_clone = new_val;
val_clone: {
handler(current) {
this.$emit('input', current);
},
deep: true
}
}
}
......
......@@ -13,10 +13,8 @@
<tr>
<th class="dragcolumn"></th>
<th>{{ $gettext('Aussagen') }}</th>
<template v-if="typeof val_clone.maximum !== 'undefined' && typeof val_clone.minimum !== 'undefined'">
<th v-for="i in (val_clone.maximum - val_clone.minimum + 1)" :key="i" class="number">{{ (val_clone.minimum - 1 + i) }}</th>
</template>
<th v-if="typeof val_clone.alternative_answer !== 'undefined' && val_clone.alternative_answer.trim().length > 0">{{ val_clone.alternative_answer }}</th>
<th v-if="val_clone.alternative_answer.trim().length > 0">{{ val_clone.alternative_answer }}</th>
<th class="actions"></th>
</tr>
</thead>
......@@ -25,7 +23,7 @@
<td class="dragcolumn">
<a class="dragarea"
tabindex="0"
:title="$gettextInterpolate('Sortierelement für Aussage %{statement}. Drücken Sie die Tasten Pfeil-nach-oben oder Pfeil-nach-unten, um dieses Element in der Liste zu verschieben.', {statement: statement})"
:title="$gettextInterpolate($gettext('Sortierelement für Aussage %{statement}. Drücken Sie die Tasten Pfeil-nach-oben oder Pfeil-nach-unten, um dieses Element in der Liste zu verschieben.'), {statement: statement})"
@keydown="keyHandler($event, index)"
:ref="'draghandle_' + index">
<span class="drag-handle"></span>
......@@ -38,29 +36,31 @@
@paste="(ev) => onPaste(ev, index)"
v-model="val_clone.statements[index]">
</td>
<template v-if="typeof val_clone.maximum !== 'undefined' && typeof val_clone.minimum !== 'undefined'">
<td v-for="i in (val_clone.maximum - value.minimum + 1)" :key="i">
<input type="radio" value="1" disabled :title="i + val_clone.minimum - 1">
<td v-for="i in (val_clone.maximum - val_clone.minimum + 1)" :key="i">
<input type="radio" disabled :title="i + val_clone.minimum - 1">
</td>
</template>
<td v-if="typeof val_clone.alternative_answer !== 'undefined' && val_clone.alternative_answer.trim().length > 0 > 0">
<input type="radio" value="1" disabled :title="val_clone.alternative_answer">
<td v-if="val_clone.alternative_answer.trim().length > 0">
<input type="radio" disabled :title="val_clone.alternative_answer">
</td>
<td class="actions">
<button class="as-link"
@click.prevent="askForDeletingStatement(index)"
:title="$gettext('Aussage löschen')">
<studip-icon shape="trash" role="clickable" :size="20" alt=""></studip-icon>
</button>
<studip-icon name="delete"
shape="trash"
:size="20"
@click.prevent="deleteStatement(index)"
:title="$gettext('Aussage löschen')"
></studip-icon>
</td>
</tr>
</draggable>
<tfoot>
<tr>
<td :colspan="val_clone.maximum - val_clone.minimum + 4 + (typeof val_clone.alternative_answer !== 'undefined' && val_clone.alternative_answer.trim().length > 0 ? 1 : 0)">
<button @click.prevent="addStatement" class="as-link" :title="$gettext('Aussage hinzufügen')">
<studip-icon shape="add" role="clickable" :size="20" alt=""></studip-icon>
</button>
<td :colspan="val_clone.maximum - val_clone.minimum + 4 + (val_clone.alternative_answer.trim().length > 0 ? 1 : 0)">
<studip-icon name="add"
shape="add"
:size="20"
@click.prevent="addStatement()"
:title="$gettext('Aussage hinzufügen')"
></studip-icon>
</td>
</tr>
</tfoot>
......@@ -77,59 +77,44 @@
<label>
{{ $gettext('Maximum') }}
<input type="number" v-model.number="val_clone.maximum">
<input type="number" v-model.number="val_clone.maximum" :min="val_clone.minimum">
</label>
<label>
{{ $gettext('Minimum') }}
<input type="number" v-model.number="val_clone.minimum">
<input type="number" v-model.number="val_clone.minimum" min="1">
</label>
<label>
{{ $gettext('Ausweichantwort (leer lassen für keine)') }}
<input type="text" v-model="val_clone.alternative_answer">
</label>
<studip-dialog
v-if="askForDeleting"
:title="$gettext('Bitte bestätigen Sie die Aktion.')"
:question="$gettext('Wirklich löschen?')"
:confirmText="$gettext('Ja')"
:closeText="$gettext('Nein')"
closeClass="cancel"
height="180"
@confirm="deleteStatement"
@close="askForDeleting = false"
>
</studip-dialog>
</div>
</template>
<script>
import StudipIcon from "../StudipIcon.vue";
import StudipDialog from "../StudipDialog.vue";
import draggable from 'vuedraggable';
import StudipWysiwyg from "../StudipWysiwyg.vue";
const default_value = {
const default_value = () => ({
description: '',
statements: ['', '', '', ''],
mandatory: false,
randomize: false,
minimum: 1,
maximum: 5,
alternative_answer: ''
};
});
export default {
name: 'likert-edit',
components: {
StudipIcon,
StudipDialog,
draggable,
StudipWysiwyg
},
props: {
value: {
type: Object,
required: false,
default: function () {
return default_value;
default() {
return default_value();
}
},
question_id: {
......@@ -137,46 +122,30 @@ export default {
required: false
}
},
data: function () {
data() {
return {
val_clone: {},
askForDeleting: false,
indexOfDeletingStatement: 0,
val_clone: null,
assistiveLive: ''
};
},
methods: {
addStatement: function (val, position) {
if (val.target) {
val = '';
}
let data = this.value;
if (typeof position === "undefined") {
data.statements.push(val || '');
position = this.value.length - 1
addStatement(val = '', position = null) {
if (position === null) {
this.val_clone.statements.push(val || '');
} else {
data.statements.splice(position, 0, val || '');
this.val_clone.statements.splice(position, 0, val || '');
}
this.$emit('input', data);
let v = this;
this.$nextTick(function () {
v.$refs['statement_' + (v.value.statements.length - 1)][0].focus();
this.$nextTick(() => {
this.$refs['statement_' + (v.value.statements.length - 1)][0].focus();
});
},
askForDeletingStatement: function (index) {
this.indexOfDeletingStatement = index;
if (this.value.statements[index]) {
this.askForDeleting = true;
} else {
this.deleteStatement();
}
},
deleteStatement: function () {
this.$delete(this.value.statements, this.indexOfDeletingStatement);
this.askForDeleting = false;
deleteStatement(index) {
STUDIP.Dialog.confirm(this.$gettext('Wirklich löschen?')).done(() => {
this.$delete(this.value.statements, index);
});
},
onPaste: function (ev, position) {
let data = ev.clipboardData.getData("text").split("\n");
onPaste(ev, position) {
let data = ev.clipboardData.getData('text').split("\n");
for (let i = 0; i < data.length; i++) {
if (data[i].trim()) {
this.addStatement(data[i], position + i);
......@@ -189,11 +158,11 @@ export default {
e.preventDefault();
if (index > 0) {
this.moveUp(index);
this.$nextTick(function () {
this.$nextTick(() => {
this.$refs['draghandle_' + (index - 1)][0].focus();
this.assistiveLive = this.$gettextInterpolate(
'Aktuelle Position in der Liste: %{pos} von %{listLength}.'
, {pos: index, listLength: this.val_clone.statements.length}
this.$gettext('Aktuelle Position in der Liste: %{pos} von %{listLength}.'),
{pos: index, listLength: this.val_clone.statements.length}
);
});
}
......@@ -202,40 +171,46 @@ export default {
e.preventDefault();
if (index < this.val_clone.statements.length - 1) {
this.moveDown(index);
this.$nextTick(function () {
this.$nextTick(() => {
this.$refs['draghandle_' + (index + 1)][0].focus();
this.assistiveLive = this.$gettextInterpolate(
'Aktuelle Position in der Liste: %{pos} von %{listLength}.'
, {pos: index + 2, listLength: this.val_clone.statements.length}
this.$gettext('Aktuelle Position in der Liste: %{pos} von %{listLength}.'),
{pos: index + 2, listLength: this.val_clone.statements.length}
);
});
}
break;
}
},
moveDown: function (index) {
let statement = this.val_clone.statements[index];
this.val_clone.statements[index] = this.val_clone.statements[index + 1];
this.val_clone.statements[index + 1] = statement;
this.$forceUpdate();
moveDown(index) {
this.val_clone.statements.splice(
index,
2,
this.val_clone.statements[index + 1],
this.val_clone.statements[index]
);
},
moveUp: function (index) {
let statement = this.val_clone.statements[index];
this.val_clone.statements[index] = this.val_clone.statements[index - 1];
this.val_clone.statements[index - 1] = statement;
this.$forceUpdate();
}
moveUp(index) {
this.val_clone.statements.splice(
index - 1,
2,
this.val_clone.statements[index],
this.val_clone.statements[index - 1]
);
},
mounted: function () {
this.val_clone = this.value;
if (!this.value.statements) {
this.$emit('input', default_value);
}
},
created() {
this.val_clone = Object.assign({}, default_value(), this.value ?? {});
},
mounted() {
this.$refs.autofocus.focus();
},
watch: {
value (new_val) {
this.val_clone = new_val;
val_clone: {
handler(current) {
this.$emit('input', current);
},
deep: true
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment