diff options
| author | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
|---|---|---|
| committer | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
| commit | 4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch) | |
| tree | 5c07151ae61276d334e88f6309c30d439a85c12e /resources/vue/components/questionnaires/InputArray.vue | |
| parent | da0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff) | |
| parent | 97a188592c679890a25c37ab78463add76a52ff7 (diff) | |
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'resources/vue/components/questionnaires/InputArray.vue')
| -rw-r--r-- | resources/vue/components/questionnaires/InputArray.vue | 279 |
1 files changed, 136 insertions, 143 deletions
diff --git a/resources/vue/components/questionnaires/InputArray.vue b/resources/vue/components/questionnaires/InputArray.vue index 37fa6fc..896418f 100644 --- a/resources/vue/components/questionnaires/InputArray.vue +++ b/resources/vue/components/questionnaires/InputArray.vue @@ -1,177 +1,170 @@ <template> <div class="input-array"> <span aria-live="assertive" class="sr-only">{{ assistiveLive }}</span> - <draggable v-model="options" handle=".dragarea" tag="ol" class="clean options"> - <li v-for="(option, index) in options" :key="index"> - <a class="dragarea" - v-if="options.length > 1" - tabindex="0" - :ref="'draghandle_' + index" - :title="$gettextInterpolate('Sortierelement für Option %{option}. Drücken Sie die Tasten Pfeil-nach-oben oder Pfeil-nach-unten, um dieses Element in der Liste zu verschieben.', {option: option})" - @keydown="keyHandler($event, index)"> - <span class="drag-handle"></span> - </a> - <input type="text" - :placeholder="$gettext('Option')" - :ref="'option_' + index" - @paste="(ev) => onPaste(ev, index)" - v-model="options[index]"> - <button class="as-link" - :title="$gettext('Option löschen')" - @click.prevent="askForDeletingOption(index)"> - <studip-icon shape="trash" role="clickable" :size="20" alt=""></studip-icon> - </button> - <button v-if="index == options.length - 1" - class="as-link" - :title="$gettext('Option hinzufügen')" - @click.prevent="addOption"> - <studip-icon shape="add" role="clickable" :size="20" alt=""></studip-icon> - </button> - </li> - </draggable> - <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="deleteOption" - @close="askForDeleting = false" - > - </studip-dialog> + <table class="default nohover"> + <colgroup> + <col style="width: 16px"> + <col> + <col v-for="i in additionalColspan" :key="`colspan-${i}`"> + <col style="width: 24px"> + </colgroup> + <thead> + <tr> + <th class="dragcolumn"></th> + <th>{{ labelPlural }}</th> + <slot name="header-cells" /> + <th class="actions"></th> + </tr> + </thead> + <Draggable v-model="options" handle=".dragarea" tag="tbody" class="statements"> + <tr v-for="(option, index) in options" :key="index"> + <td class="dragcolumn"> + <a class="dragarea" + tabindex="0" + :title="$gettextInterpolate($gettext(`Sortierelement für %{label} %{option}. Drücken Sie die Tasten Pfeil-nach-oben oder Pfeil-nach-unten, um dieses Element in der Liste zu verschieben.`), {option, label}, true)" + @keydown="keyHandler($event, index)" + ref="draghandle"> + <span class="drag-handle"></span> + </a> + </td> + <td> + <input type="text" + ref="inputs" + :placeholder="label" + @paste="(ev) => onPaste(ev, index)" + v-model="options[index]"> + </td> + <slot name="body-cells" /> + <td class="actions"> + <StudipIcon name="delete" + shape="trash" + :size="20" + @click.prevent="deleteOption(index)" + :title="$gettextInterpolate($gettext('%{label} löschen'), {label}, true)" + /> + </td> + </tr> + </Draggable> + <tfoot> + <tr> + <td :colspan="3 + additionalColspan"> + <button class="as-link" + :title="$gettextInterpolate($gettext('%{label} hinzufügen'), {label}, true)" + @click.prevent="addOption()"> + <StudipIcon shape="add" :size="20" alt="" /> + </button> + </td> + </tr> + </tfoot> + </table> </div> </template> <script> -import StudipIcon from "../StudipIcon.vue"; -import StudipDialog from "../StudipDialog.vue"; -import draggable from 'vuedraggable'; +import Draggable from 'vuedraggable'; +import { $gettext } from '../../../assets/javascripts/lib/gettext'; + export default { name: 'input-array', - components: { - StudipIcon, - StudipDialog, - draggable - }, + components: { Draggable }, props: { - value: { - type: Array, - required: false - } + additionalColspan: { + type: Number, + default: 0, + }, + label: { + type: String, + default: $gettext('Option'), + }, + labelPlural: { + type: String, + default: $gettext('Optionen'), + }, + value: Array, }, - data: function () { + data() { return { options: [], - askForDeleting: false, - indexOfDeletingOption: 0, - unique_id: null, - assistiveLive: '' + assistiveLive: '', }; }, methods: { - addOption: function (val, position) { - let data = this.value; - if (val.target) { - val = ''; - } - if (typeof position === "undefined") { - data.push(val || ''); - position = this.value.length - 1 - } else { - data.splice(position, 0, val || ''); - } - this.$emit('input', data); - let v = this; - this.$nextTick(function () { - v.$refs['option_' + position][0].focus(); + addOption(val = '', position = this.options.length) { + this.$set(this.options, position, val.trim()); + + this.$nextTick(() => { + this.$refs.inputs[position].focus(); }); }, - askForDeletingOption: function (index) { - this.indexOfDeletingOption = index; - if (this.value[index]) { - this.askForDeleting = true; - } else { - this.deleteOption(); - } - }, - deleteOption: function () { - this.$delete(this.value, this.indexOfDeletingOption); - this.askForDeleting = false; + deleteOption(index) { + const question = this.options[index] ? this.$gettext('Wirklich löschen?') : true; + STUDIP.Dialog.confirm(question).done(() => { + this.$delete(this.options, index); + }); }, - onPaste: function (ev, position) { - let data = ev.clipboardData.getData("text").split("\n"); - for (let i = 0; i < data.length; i++) { - if (data[i].trim()) { - this.addOption(data[i], position + i); - } - } + onPaste(ev, position) { + ev.clipboardData + .getData('text') + .split("\n") + .filter(str => str.trim().length > 0) + .forEach((value, index) => this.addOption(value, position + index)); + ev.preventDefault(); }, keyHandler(e, index) { - switch (e.keyCode) { - case 38: // up - e.preventDefault(); - if (index > 0) { - this.moveUp(index); - this.$nextTick(function () { - this.$refs['draghandle_' + (index - 1)][0].focus(); - this.assistiveLive = this.$gettextInterpolate( - 'Aktuelle Position in der Liste: %{pos} von %{listLength}.' - , {pos: index, listLength: this.options.length} - ); - }); - } - break; - case 40: // down - e.preventDefault(); - if (index < this.options.length - 1) { - this.moveDown(index); - this.$nextTick(function () { - this.$refs['draghandle_' + (index + 1)][0].focus(); - this.assistiveLive = this.$gettextInterpolate( - 'Aktuelle Position in der Liste: %{pos} von %{listLength}.' - , {pos: index + 2, listLength: this.options.length} - ); - }); - } - break; - } - }, - moveDown: function (index) { - if (index == this.options.length - 1) { + if (e.keyCode !== 38 && e.keyCode !== 40) { return; } - let option = this.options[index]; - this.options[index] = this.options[index + 1]; - this.options[index + 1] = option; - this.$forceUpdate(); + + e.preventDefault(); + + const moveUp = e.keyCode === 38; + + this.moveElement(index, moveUp ? -1 : 1).then((newIndex) => { + this.assistiveLive = this.$gettextInterpolate( + this.$gettext('Aktuelle Position in der Liste: %{pos} von %{listLength}.'), + {pos: newIndex + 1, listLength: this.options.length} + ); + + this.$nextTick(() => { + this.$refs['draghandle'][newIndex].focus(); + }); + }) }, - moveUp: function (index) { - if (index === 0) { - return; + moveElement(index, direction) { + if (this.options[index + direction] === undefined) { + return Promise.resolve(index); } - let option = this.options[index]; - this.options[index] = this.options[index - 1]; - this.options[index - 1] = option; - this.$forceUpdate(); + + const indices = [index, index + direction].sort(); + + this.options.splice( + Math.min(...indices), + 2, + ...indices.reverse().map(idx => this.options[idx]) + ); + + return Promise.resolve(index + direction); } }, - mounted: function () { - this.options = this.value; - this.unique_id = 'array_input_' + Math.floor(Math.random() * 100000000); - }, watch: { - options (new_data, old_data) { - if (typeof old_data === 'undefined' || typeof new_data === 'undefined') { - return; - } - this.$emit('input', new_data); + options: { + handler(current) { + this.$emit('input', current); + }, + deep: true }, - value (new_val) { - this.options = new_val; + value: { + handler(current) { + this.options = current; + }, + immediate: true } } } </script> +<style scoped> +.input-array input[type="text"] { + max-width: unset; +} +</style> |
