Skip to content

Commit 61af6bc

Browse files
hamza221Chartman123
authored andcommitted
Add multiple options with one paste #337
Signed-off-by: hamza221 <[email protected]> Signed-off-by: Christian Hartmann <[email protected]>
1 parent 0d2ec25 commit 61af6bc

File tree

4 files changed

+300
-71
lines changed

4 files changed

+300
-71
lines changed

src/components/OptionInputDialog.vue

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<!--
2+
- @copyright Copyright (c) 2024 Christian Hartmann <[email protected]>
3+
-
4+
- @author Christian Hartmann <[email protected]>
5+
-
6+
- @license AGPL-3.0-or-later
7+
-
8+
- This program is free software: you can redistribute it and/or modify
9+
- it under the terms of the GNU Affero General Public License as
10+
- published by the Free Software Foundation, either version 3 of the
11+
- License, or (at your option) any later version.
12+
-
13+
- This program is distributed in the hope that it will be useful,
14+
- but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
- GNU Affero General Public License for more details.
17+
-
18+
- You should have received a copy of the GNU Affero General Public License
19+
- along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
-
21+
-->
22+
23+
<template>
24+
<NcDialog
25+
content-classes="options-modal"
26+
:name="t('forms', 'Add multiple options')"
27+
:open="open"
28+
:buttons="buttons"
29+
size="normal"
30+
@update:open="$emit('update:open', $event)">
31+
<NcTextArea
32+
:value.sync="enteredOptions"
33+
:label="t('forms', 'Add multiple options (one per line)')"
34+
:placeholder="t('forms', 'Add multiple options (one per line)')"
35+
resize="vertical"
36+
rows="10" />
37+
<NcSelect
38+
:input-label="t('forms', 'Options')"
39+
multiple
40+
disabled
41+
:value="multipleOptions" />
42+
</NcDialog>
43+
</template>
44+
45+
<script>
46+
import { showError } from '@nextcloud/dialogs'
47+
import { translate as t } from '@nextcloud/l10n'
48+
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
49+
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
50+
import NcTextArea from '@nextcloud/vue/dist/Components/NcTextArea.js'
51+
52+
import IconCheck from '@mdi/svg/svg/check.svg?raw'
53+
54+
import { defineComponent } from 'vue'
55+
56+
export default defineComponent({
57+
name: 'OptionInputDialog',
58+
59+
components: {
60+
NcDialog,
61+
NcSelect,
62+
NcTextArea,
63+
},
64+
65+
props: {
66+
open: {
67+
type: Boolean,
68+
required: true,
69+
},
70+
},
71+
72+
emits: ['update:open'],
73+
74+
data() {
75+
return {
76+
enteredOptions: '',
77+
}
78+
},
79+
80+
computed: {
81+
buttons() {
82+
return [
83+
{
84+
label: t('forms', 'Cancel'),
85+
callback: () => {
86+
this.$emit('update:open', false)
87+
},
88+
},
89+
{
90+
label: t('forms', 'Add options'),
91+
type: 'primary',
92+
icon: IconCheck,
93+
callback: () => {
94+
this.onMultipleOptions()
95+
},
96+
},
97+
]
98+
},
99+
100+
multipleOptions() {
101+
const allOptions = this.enteredOptions.split(/\r?\n/g)
102+
return allOptions.filter((answer) => {
103+
return answer.trim().length > 0
104+
})
105+
},
106+
},
107+
108+
methods: {
109+
t,
110+
111+
onMultipleOptions() {
112+
this.$emit('update:open', false)
113+
if (this.multipleOptions.length > 1) {
114+
// extract all options entries to parent
115+
this.$emit('multiple-answers', this.multipleOptions)
116+
this.enteredOptions = ''
117+
return
118+
}
119+
// in case of only one option, just show an error message because it is probably missuse of the feature
120+
showError(t('forms', 'Options should be seperated by new line!'))
121+
},
122+
},
123+
})
124+
</script>
125+
126+
<style scoped>
127+
:deep(.options-modal) {
128+
padding-block: 0px 12px;
129+
padding-inline: 8px 20px;
130+
}
131+
132+
:deep(.v-select) {
133+
width: 100%;
134+
margin-top: 10px !important;
135+
display: flex;
136+
flex-direction: column;
137+
gap: 2px 0;
138+
}
139+
</style>

src/components/Questions/QuestionDropdown.vue

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
@update:checked="onShuffleOptionsChange">
3535
{{ t('forms', 'Shuffle options') }}
3636
</NcActionCheckbox>
37+
<NcActionButton close-after-click @click="isOptionDialogShown = true">
38+
<template #icon>
39+
<IconContentPaste :size="20" />
40+
</template>
41+
{{ t('forms', 'Add multiple options') }}
42+
</NcActionButton>
3743
</template>
3844
<NcSelect
3945
v-if="readOnly"
@@ -47,37 +53,47 @@
4753
label="text"
4854
@input="onInput" />
4955

50-
<ol v-if="!readOnly" class="question__content">
51-
<!-- Answer text input edit -->
52-
<AnswerInput
53-
v-for="(answer, index) in options"
54-
:key="
55-
index /* using index to keep the same vnode after new answer creation */
56-
"
57-
ref="input"
58-
:answer="answer"
59-
:index="index"
60-
:is-unique="!isMultiple"
61-
:is-dropdown="true"
62-
:max-option-length="maxStringLengths.optionText"
63-
@delete="deleteOption"
64-
@update:answer="updateAnswer"
65-
@focus-next="focusNextInput"
66-
@tabbed-out="checkValidOption" />
67-
68-
<li v-if="!isLastEmpty || hasNoAnswer" class="question__item">
69-
<input
70-
ref="pseudoInput"
71-
v-model="inputValue"
72-
:aria-label="t('forms', 'Add a new answer')"
73-
:placeholder="t('forms', 'Add a new answer')"
74-
class="question__input"
75-
:maxlength="maxStringLengths.optionText"
76-
minlength="1"
77-
type="text"
78-
@input="addNewEntry" />
79-
</li>
80-
</ol>
56+
<template v-else>
57+
<div v-if="isLoading">
58+
<NcLoadingIcon :size="64" />
59+
</div>
60+
<ol v-else class="question__content">
61+
<!-- Answer text input edit -->
62+
<AnswerInput
63+
v-for="(answer, index) in options"
64+
:key="
65+
index /* using index to keep the same vnode after new answer creation */
66+
"
67+
ref="input"
68+
:answer="answer"
69+
:index="index"
70+
:is-unique="!isMultiple"
71+
:is-dropdown="true"
72+
:max-option-length="maxStringLengths.optionText"
73+
@delete="deleteOption"
74+
@update:answer="updateAnswer"
75+
@focus-next="focusNextInput"
76+
@tabbed-out="checkValidOption" />
77+
78+
<li v-if="!isLastEmpty || hasNoAnswer" class="question__item">
79+
<input
80+
ref="pseudoInput"
81+
v-model="inputValue"
82+
:aria-label="t('forms', 'Add a new answer')"
83+
:placeholder="t('forms', 'Add a new answer')"
84+
class="question__input"
85+
:maxlength="maxStringLengths.optionText"
86+
minlength="1"
87+
type="text"
88+
@input="addNewEntry" />
89+
</li>
90+
</ol>
91+
</template>
92+
93+
<!-- Add multiple options modal -->
94+
<OptionInputDialog
95+
:open.sync="isOptionDialogShown"
96+
@multiple-answers="handleMultipleOptions" />
8197
</Question>
8298
</template>
8399

@@ -87,9 +103,14 @@ import { emit } from '@nextcloud/event-bus'
87103
import { generateOcsUrl } from '@nextcloud/router'
88104
import axios from '@nextcloud/axios'
89105
import NcActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js'
106+
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
107+
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
90108
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
91109
110+
import IconContentPaste from 'vue-material-design-icons/ContentPaste.vue'
111+
92112
import AnswerInput from './AnswerInput.vue'
113+
import OptionInputDialog from '../OptionInputDialog.vue'
93114
import QuestionMixin from '../../mixins/QuestionMixin.js'
94115
import GenRandomId from '../../utils/GenRandomId.js'
95116
import logger from '../../utils/Logger.js'
@@ -99,8 +120,12 @@ export default {
99120
100121
components: {
101122
AnswerInput,
123+
IconContentPaste,
124+
NcActionButton,
102125
NcActionCheckbox,
126+
NcLoadingIcon,
103127
NcSelect,
128+
OptionInputDialog,
104129
},
105130
106131
mixins: [QuestionMixin],
@@ -109,6 +134,8 @@ export default {
109134
return {
110135
selectedOption: null,
111136
inputValue: '',
137+
isOptionDialogShown: false,
138+
isLoading: false,
112139
}
113140
},
114141

0 commit comments

Comments
 (0)