Skip to content

Commit

Permalink
feat: Allow reordering questions using the keyboard
Browse files Browse the repository at this point in the history
Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Feb 28, 2023
1 parent 4b9adef commit 86f357a
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 13 deletions.
63 changes: 61 additions & 2 deletions src/components/Questions/Question.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,27 @@
:class="{'question__drag-handle--shiftup': shiftDragHandle}"
:aria-label="t('forms', 'Drag to reorder the questions')"
role="button">
<NcButton ref="buttonU"
:aria-label="t('forms', 'Move question up')"
:disabled="!canMoveUp"
type="tertiary-no-background"
@click.stop="onMoveUp"
@keyup.space.stop="onMoveUp">
<template #icon>
<IconArrowUp :size="20" />
</template>
</NcButton>
<IconDragHorizontalVariant :size="20" />
<NcButton ref="buttonDown"
:aria-label="t('forms', 'Move question down')"
:disabled="!canMoveDown"
type="tertiary-no-background"
@click.stop="onMoveDown"
@keyup.space.stop="onMoveDown">
<template #icon>
<IconArrowDown :size="20" />
</template>
</NcButton>
</div>

<!-- Header -->
Expand Down Expand Up @@ -99,9 +119,12 @@ import { directive as ClickOutside } from 'v-click-outside'
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcRichContenteditable from '@nextcloud/vue/dist/Components/NcRichContenteditable.js'
import IconAlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
import IconArrowDown from 'vue-material-design-icons/ArrowDown.vue'
import IconArrowUp from 'vue-material-design-icons/ArrowUp.vue'
import IconDelete from 'vue-material-design-icons/Delete.vue'
import IconDragHorizontalVariant from 'vue-material-design-icons/DragHorizontalVariant.vue'
Expand All @@ -114,11 +137,14 @@ export default {
components: {
IconAlertCircleOutline,
IconArrowDown,
IconArrowUp,
IconDelete,
IconDragHorizontalVariant,
NcActions,
NcActionButton,
NcActionCheckbox,
NcButton,
NcRichContenteditable,
},
Expand Down Expand Up @@ -169,6 +195,14 @@ export default {
type: String,
default: t('forms', 'This question needs a title!'),
},
canMoveDown: {
type: Boolean,
default: false,
},
canMoveUp: {
type: Boolean,
default: false,
},
},
computed: {
Expand Down Expand Up @@ -219,6 +253,18 @@ export default {
this.$emit('update:isRequired', isRequired)
},
/**
* Reorder question but keep focus on the button
*/
onMoveDown() {
this.$emit('move-down')
this.$nextTick(() => this.$refs.buttonDown.$el.focus())
},
onMoveUp() {
this.$emit('move-up')
this.$nextTick(() => this.$refs.buttonU.$el.focus())
},
/**
* Enable the edit mode
*/
Expand Down Expand Up @@ -266,20 +312,33 @@ export default {
&__drag-handle {
position: absolute;
display: flex;
left: 0;
flex-direction: column;
justify-content: space-around;
left: -10px;
width: 44px;
height: 100%;
opacity: .5;
cursor: grab;
.button-vue {
position: absolute;
top: -9999px;
}
// Avoid moving drag-handle due to newAnswer-input on multiple-Questions
&--shiftup {
height: calc(100% - 44px);
}
&:hover,
&:focus {
&:focus,
&:focus-within {
opacity: 1;
.button-vue {
position: initial;
top: initial;
}
}
&:active {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Questions/QuestionDate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
:max-string-lengths="maxStringLengths"
:title-placeholder="answerType.titlePlaceholder"
:warning-invalid="answerType.warningInvalid"
v-on="$listeners"
@update:text="onTitleChange"
@update:description="onDescriptionChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
@update:isRequired="onRequiredChange">
<div class="question__content">
<NcDatetimePicker v-model="time"
:disabled="!readOnly"
Expand Down
4 changes: 2 additions & 2 deletions src/components/Questions/QuestionDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
:warning-invalid="answerType.warningInvalid"
:content-valid="contentValid"
:shift-drag-handle="shiftDragHandle"
v-on="$listeners"
@update:text="onTitleChange"
@update:description="onDescriptionChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
@update:isRequired="onRequiredChange">
<template #actions>
<NcActionCheckbox :checked="extraSettings?.shuffleOptions"
@update:checked="onShuffleOptionsChange">
Expand Down
4 changes: 2 additions & 2 deletions src/components/Questions/QuestionLong.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
:max-string-lengths="maxStringLengths"
:title-placeholder="answerType.titlePlaceholder"
:warning-invalid="answerType.warningInvalid"
v-on="$listeners"
@update:text="onTitleChange"
@update:description="onDescriptionChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
@update:isRequired="onRequiredChange">
<div class="question__content">
<textarea ref="textarea"
:aria-label="t('forms', 'A long answer for the question “{text}”', { text })"
Expand Down
4 changes: 2 additions & 2 deletions src/components/Questions/QuestionMultiple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
:warning-invalid="answerType.warningInvalid"
:content-valid="contentValid"
:shift-drag-handle="shiftDragHandle"
v-on="$listeners"
@update:text="onTitleChange"
@update:description="onDescriptionChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
@update:isRequired="onRequiredChange">
<template #actions>
<NcActionCheckbox :checked="extraSettings?.shuffleOptions"
@update:checked="onShuffleOptionsChange">
Expand Down
4 changes: 2 additions & 2 deletions src/components/Questions/QuestionShort.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
:max-string-lengths="maxStringLengths"
:title-placeholder="answerType.titlePlaceholder"
:warning-invalid="answerType.warningInvalid"
v-on="$listeners"
@update:text="onTitleChange"
@update:description="onDescriptionChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
@update:isRequired="onRequiredChange">
<div class="question__content">
<input ref="input"
:aria-label="t('forms', 'A short answer for the question “{text}”', { text })"
Expand Down
18 changes: 17 additions & 1 deletion src/views/Create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,15 @@
v-for="(question, index) in form.questions"
ref="questions"
:key="question.id"
:can-move-down="index < (form.questions.length - 1)"
:can-move-up="index > 0"
:answer-type="answerTypes[question.type]"
:index="index + 1"
:max-string-lengths="maxStringLengths"
v-bind.sync="form.questions[index]"
@delete="deleteQuestion(question)" />
@delete="deleteQuestion(question)"
@move-down="onMoveDown(index)"
@move-up="onMoveUp(index)" />
</Draggable>
<!-- Add new questions menu -->
Expand Down Expand Up @@ -268,6 +272,18 @@ export default {
},
methods: {
onMoveUp(index) {
if (index > 0) {
[this.form.questions[index - 1], this.form.questions[index]] = [this.form.questions[index], this.form.questions[index - 1]]
this.onQuestionOrderChange()
}
},
onMoveDown(index) {
// only if not the last one
if (index < (this.form.questions.length - 1)) {
this.onMoveUp(index + 1)
}
},
onTitleChange() {
this.autoSizeTitle()
this.saveTitle()
Expand Down

0 comments on commit 86f357a

Please sign in to comment.