|
21 | 21 | dir="auto" |
22 | 22 | @input="debounceOnInput" |
23 | 23 | @keydown.delete="deleteEntry" |
24 | | - @keydown.enter.prevent="focusNextInput" |
| 24 | + @keydown.enter.prevent="onEnter" |
25 | 25 | @compositionstart="onCompositionStart" |
26 | 26 | @compositionend="onCompositionEnd" /> |
27 | 27 |
|
|
64 | 64 | </template> |
65 | 65 | </NcButton> |
66 | 66 | </div> |
| 67 | + <div v-else class="option__actions"> |
| 68 | + <NcButton |
| 69 | + :aria-label="t('forms', 'Add a new answer option')" |
| 70 | + variant="tertiary" |
| 71 | + :disabled="isIMEComposing || !canCreateLocalAnswer" |
| 72 | + @click="createLocalAnswer"> |
| 73 | + <template #icon> |
| 74 | + <IconPlus :size="20" /> |
| 75 | + </template> |
| 76 | + </NcButton> |
| 77 | + </div> |
67 | 78 | </li> |
68 | 79 | </template> |
69 | 80 |
|
@@ -98,6 +109,7 @@ export default { |
98 | 109 | IconCheckboxBlankOutline, |
99 | 110 | IconDelete, |
100 | 111 | IconDragIndicator, |
| 112 | + IconPlus, |
101 | 113 | IconRadioboxBlank, |
102 | 114 | IconTableColumn, |
103 | 115 | IconTableRow, |
@@ -167,6 +179,10 @@ export default { |
167 | 179 | }, |
168 | 180 |
|
169 | 181 | computed: { |
| 182 | + canCreateLocalAnswer() { |
| 183 | + return !!this.answer.text?.trim() |
| 184 | + }, |
| 185 | +
|
170 | 186 | ariaLabel() { |
171 | 187 | if (this.answer.local) { |
172 | 188 | if (this.optionType === OptionType.Column) { |
@@ -266,30 +282,65 @@ export default { |
266 | 282 | * @param {InputEvent} event The input event that triggered adding a new entry |
267 | 283 | */ |
268 | 284 | async onInput({ target, isComposing }) { |
| 285 | + if (this.answer.local) { |
| 286 | + this.$set(this.answer, 'text', target.value) |
| 287 | + return |
| 288 | + } |
| 289 | +
|
269 | 290 | if (!isComposing && !this.isIMEComposing && target.value !== '') { |
270 | 291 | // clone answer |
271 | 292 | const answer = { ...this.answer } |
272 | 293 | answer.text = this.$refs.input.value |
273 | 294 |
|
274 | | - if (this.answer.local) { |
275 | | - // Dispatched for creation. Marked as synced |
276 | | - this.$set(this.answer, 'local', false) |
277 | | - const newAnswer = await this.createAnswer(answer) |
| 295 | + await this.updateAnswer(answer) |
278 | 296 |
|
279 | | - // Forward changes, but use current answer.text to avoid erasing |
280 | | - // any in-between changes while creating the answer |
281 | | - newAnswer.text = this.$refs.input.value |
| 297 | + // Forward changes, but use current answer.text to avoid erasing |
| 298 | + // any in-between changes while updating the answer |
| 299 | + answer.text = this.$refs.input.value |
| 300 | + this.$emit('update:answer', this.index, answer) |
| 301 | + } |
| 302 | + }, |
282 | 303 |
|
283 | | - this.$emit('create-answer', this.index, newAnswer) |
284 | | - } else { |
285 | | - await this.updateAnswer(answer) |
| 304 | + /** |
| 305 | + * Handle Enter key: create local answer or move focus |
| 306 | + * |
| 307 | + * @param {KeyboardEvent} e the keydown event |
| 308 | + */ |
| 309 | + onEnter(e) { |
| 310 | + if (this.answer.local) { |
| 311 | + this.createLocalAnswer(e) |
| 312 | + return |
| 313 | + } |
| 314 | + this.focusNextInput(e) |
| 315 | + }, |
286 | 316 |
|
287 | | - // Forward changes, but use current answer.text to avoid erasing |
288 | | - // any in-between changes while updating the answer |
289 | | - answer.text = this.$refs.input.value |
290 | | - this.$emit('update:answer', this.index, answer) |
291 | | - } |
| 317 | + /** |
| 318 | + * Create a new local answer option from the current input |
| 319 | + * |
| 320 | + * @param {Event} e the triggering event |
| 321 | + */ |
| 322 | + async createLocalAnswer(e) { |
| 323 | + if (this.isIMEComposing || e?.isComposing) { |
| 324 | + return |
| 325 | + } |
| 326 | +
|
| 327 | + const value = this.$refs.input?.value ?? '' |
| 328 | + if (!value.trim()) { |
| 329 | + return |
292 | 330 | } |
| 331 | +
|
| 332 | + const answer = { ...this.answer } |
| 333 | + answer.text = value |
| 334 | +
|
| 335 | + // Dispatched for creation. Marked as synced |
| 336 | + this.$set(this.answer, 'local', false) |
| 337 | + const newAnswer = await this.createAnswer(answer) |
| 338 | +
|
| 339 | + // Forward changes, but use current answer.text to avoid erasing |
| 340 | + // any in-between changes while creating the answer |
| 341 | + newAnswer.text = this.$refs.input.value |
| 342 | +
|
| 343 | + this.$emit('create-answer', this.index, newAnswer) |
293 | 344 | }, |
294 | 345 |
|
295 | 346 | /** |
|
0 commit comments