diff --git a/CHANGELOG.en.md b/CHANGELOG.en.md
index 8177c41c6..b89c8482c 100644
--- a/CHANGELOG.en.md
+++ b/CHANGELOG.en.md
@@ -7,6 +7,10 @@
## v5.0.0 - tbd
+- **Re-order options**
+
+ Options of multiple choice questions (radio/checkbox/dropdown) can now be re-ordered using a menu or drag'n'drop.
+
- **Unified Search integration**
You can now use the Unified Search to search forms based on the title and the description.
diff --git a/src/components/Questions/AnswerInput.vue b/src/components/Questions/AnswerInput.vue
index c355b59be..174a8245a 100644
--- a/src/components/Questions/AnswerInput.vue
+++ b/src/components/Questions/AnswerInput.vue
@@ -26,43 +26,43 @@
@compositionend="onCompositionEnd" />
-
-
-
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
+ {{ t('forms', 'Move option down') }}
+
+
+
+
+
+
+
@@ -74,13 +74,17 @@ import axios from '@nextcloud/axios'
import debounce from 'debounce'
import PQueue from 'p-queue'
-import NcButton from '@nextcloud/vue/components/NcButton'
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 IconCheckboxBlankOutline from 'vue-material-design-icons/CheckboxBlankOutline.vue'
+import IconDelete from 'vue-material-design-icons/Delete.vue'
+import IconDragIndicator from '../Icons/IconDragIndicator.vue'
import IconRadioboxBlank from 'vue-material-design-icons/RadioboxBlank.vue'
+import NcActions from '@nextcloud/vue/components/NcActions'
+import NcActionButton from '@nextcloud/vue/components/NcActionButton'
+import NcButton from '@nextcloud/vue/components/NcButton'
+
import OcsResponse2Data from '../../utils/OcsResponse2Data.js'
import logger from '../../utils/Logger.js'
@@ -92,7 +96,10 @@ export default {
IconArrowUp,
IconCheckboxBlankOutline,
IconDelete,
+ IconDragIndicator,
IconRadioboxBlank,
+ NcActions,
+ NcActionButton,
NcButton,
},
@@ -145,6 +152,10 @@ export default {
})
},
+ optionDragMenuId() {
+ return `q${this.answer.questionId}o${this.answer.id}__drag_menu`
+ },
+
placeholder() {
if (this.answer.local) {
return t('forms', 'Add a new answer option')
@@ -303,7 +314,6 @@ export default {
logger.error('Error while saving answer', { answer, error })
showError(t('forms', 'Error while saving the answer'))
}
- return answer
},
/**
@@ -311,19 +321,18 @@ export default {
*/
onMoveDown() {
this.$emit('move-down')
- if (this.index < this.maxIndex - 1) {
- this.$nextTick(() => this.$refs.buttonDown.$el.focus())
- } else {
- this.$nextTick(() => this.$refs.buttonUp.$el.focus())
- }
+ this.focusButton(
+ this.index < this.maxIndex - 1
+ ? 'buttonOptionDown'
+ : 'buttonOptionUp',
+ )
},
onMoveUp() {
this.$emit('move-up')
- if (this.index > 1) {
- this.$nextTick(() => this.$refs.buttonUp.$el.focus())
- } else {
- this.$nextTick(() => this.$refs.buttonDown.$el.focus())
- }
+ this.focusButton(this.index > 1 ? 'buttonOptionUp' : 'buttonOptionDown')
+ },
+ focusButton(refName) {
+ this.$nextTick(() => this.$refs[refName].$el.focus())
},
/**
@@ -356,7 +365,7 @@ export default {
&__pseudoInput {
color: var(--color-primary-element);
- margin-inline-start: calc(-1 * var(--default-grid-baseline));
+ margin-inline-start: -2px;
z-index: 1;
}
@@ -364,15 +373,28 @@ export default {
display: flex;
position: absolute;
gap: var(--default-grid-baseline);
- inset-inline-end: 16px;
- height: var(--default-clickable-area);
+ inset-inline-end: 12px;
+ height: 100%;
}
- .option__actions-button {
+ .option__drag-handle,
+ .drag-indicator-icon {
+ color: var(--color-text-maxcontrast);
+ cursor: grab;
margin-block: auto;
- &:last-of-type {
- margin-inline: 5px;
+ &:hover,
+ &:focus,
+ &:focus-within {
+ color: var(--color-main-text);
+ }
+
+ &:active {
+ cursor: grabbing;
+ }
+
+ > * {
+ cursor: grab;
}
}
@@ -380,7 +402,6 @@ export default {
width: calc(100% - var(--default-clickable-area));
position: relative;
inset-inline-start: -12px;
- margin-block: 0 !important;
margin-inline-end: -12px !important;
&--shifted {
diff --git a/src/components/Questions/Question.vue b/src/components/Questions/Question.vue
index 2daa0beec..d4776cea4 100644
--- a/src/components/Questions/Question.vue
+++ b/src/components/Questions/Question.vue
@@ -381,7 +381,7 @@ export default {
gap: 12px;
width: var(--default-clickable-area);
height: 100%;
- opacity: 0.5;
+ color: var(--color-text-maxcontrast);
cursor: grab;
&-button {
@@ -397,7 +397,7 @@ export default {
&:hover,
&:focus,
&:focus-within {
- opacity: 1;
+ color: var(--color-main-text);
.question__drag-handle-button {
position: initial;
diff --git a/src/components/Questions/QuestionDropdown.vue b/src/components/Questions/QuestionDropdown.vue
index fd36b4f26..1e117dd01 100644
--- a/src/components/Questions/QuestionDropdown.vue
+++ b/src/components/Questions/QuestionDropdown.vue
@@ -40,27 +40,45 @@
-
-
-
-
+
+
+
+
+
+
@@ -71,6 +89,8 @@