diff --git a/examples/questionnaire/Example.vue b/examples/questionnaire/Example.vue index ff6ae858..317f5cfd 100644 --- a/examples/questionnaire/Example.vue +++ b/examples/questionnaire/Example.vue @@ -229,9 +229,6 @@ // we need to implement the "keyup" listener manually. if ($event.key === 'Enter' && this.completed && !this.submitted) { - // Set `submitted` to true so the form knows not to allow back/forward - // navigation anymore. - this.$refs.flowform.submitted = true this.onSendData() } }, @@ -250,6 +247,10 @@ }, onSendData() { + // Set `submitted` to true so the form knows not to allow back/forward + // navigation anymore. + this.$refs.flowform.submitted = true + this.submitted = true /* eslint-disable-next-line no-unused-vars */ diff --git a/examples/quiz/Example.vue b/examples/quiz/Example.vue index 6d091906..55687757 100644 --- a/examples/quiz/Example.vue +++ b/examples/quiz/Example.vue @@ -321,7 +321,11 @@ }, onQuizSubmit() { - this.submitted = true + // Set `submitted` to true so the form knows not to allow back/forward + // navigation anymore. + this.$refs.flowform.submitted = true + + this.submitted = true this.calculateScore() } } diff --git a/src/assets/css/common.css b/src/assets/css/common.css index b6aede84..853e00f3 100644 --- a/src/assets/css/common.css +++ b/src/assets/css/common.css @@ -275,8 +275,9 @@ span.faux-form { margin-top: 28px; } -span.f-sub + .f-answer.full-width, div.field-sectionbreaktype .f-answer { - margin-top: 14px +span.f-sub + .f-answer.full-width, +div.field-sectionbreaktype .f-answer { + margin-top: 16px } span.f-empty { @@ -349,7 +350,6 @@ h3, } @media screen and (max-width:767px) { - h2, .fh2 { font-size: 2.2rem; @@ -359,7 +359,6 @@ h3, } @media screen and (max-width:479px), (max-height:400px) { - h2, .fh2 { font-size: 1.4rem; diff --git a/src/components/FlowForm.vue b/src/components/FlowForm.vue index 0a2c06c5..c01cde6f 100644 --- a/src/components/FlowForm.vue +++ b/src/components/FlowForm.vue @@ -144,15 +144,15 @@ } }, mounted() { - document.addEventListener('keyup', this.onKeyListener, true) - document.addEventListener('keydown', this.onBackKeyListener) + document.addEventListener('keydown', this.onKeyDownListener) + document.addEventListener('keyup', this.onKeyUpListener, true) window.addEventListener('beforeunload', this.onBeforeUnload) this.setQuestions() }, beforeDestroy() { - document.removeEventListener('keyup', this.onKeyListener, true) - document.removeEventListener('keydown', this.onBackKeyListener) + document.removeEventListener('keydown', this.onKeyDownListener) + document.removeEventListener('keyup', this.onKeyUpListener, true) window.removeEventListener('beforeunload', this.onBeforeUnload) }, computed: { @@ -290,29 +290,54 @@ }, /** - * Global key listener, listens for Enter or Tab key events. + * Global key listeners, listen for Enter or Tab key events. */ - onKeyListener(e) { - if (e.shiftKey) { + onKeyDownListener(e) { + if (e.key !== 'Tab' || this.submitted) { return } - if (e.key === 'Enter' || e.key === 'Tab') { - e.stopPropagation() - this.emitEnter() - this.reverse = false - } - }, - - onBackKeyListener(e) { - if (e.shiftKey && e.key === 'Tab' ) { + if (e.shiftKey) { e.stopPropagation() e.preventDefault() + this.goToPreviousQuestion() + } else { + e.preventDefault() + + const q = this.activeQuestionComponent() + + if (q.shouldFocus()) { + q.focusField() + } else { + e.stopPropagation() + + this.emitTab() + this.reverse = false + } } }, + onKeyUpListener(e) { + if (e.shiftKey || ['Tab', 'Enter'].indexOf(e.key) === -1 || this.submitted) { + return + } + + const q = this.activeQuestionComponent() + + if (e.key === 'Tab' && q.shouldFocus()) { + q.focusField() + } else { + if (e.key === 'Enter') { + this.emitEnter() + } + + e.stopPropagation() + this.reverse = false + } + }, + emitEnter() { const q = this.activeQuestionComponent() @@ -325,6 +350,17 @@ } }, + emitTab() { + const q = this.activeQuestionComponent() + + if (q) { + // Send tab event to the current question component + q.onTab() + } else { + this.emitEnter() + } + }, + submit() { this.emitSubmit() this.submitted = true @@ -343,6 +379,10 @@ * can jump to it. */ isNextQuestionAvailable() { + if (this.submitted) { + return false + } + const q = this.activeQuestion if (q && !q.required) { diff --git a/src/components/Question.vue b/src/components/Question.vue index 00acc816..1cc5b10e 100644 --- a/src/components/Question.vue +++ b/src/components/Question.vue @@ -153,6 +153,20 @@ this.focusField() }, + shouldFocus() { + const q = this.$refs.questionComponent + + return q && q.canReceiveFocus && !q.focused + }, + + returnFocus() { + const q = this.$refs.questionComponent + + if (this.shouldFocus()) { + this.focusField() + } + }, + /** * Emits "answer" event and calls "onEnter" method on Enter press */ @@ -163,10 +177,22 @@ if (!q.focused) { this.$emit('answer', q) } + q.onEnter() } }, + onTab($event) { + const q = this.$refs.questionComponent + + if (q) { + this.returnFocus() + this.$emit('answer', q) + + q.onEnter() + } + }, + /** * Check if the "OK" button should be shown. */ diff --git a/src/components/QuestionTypes/BaseType.vue b/src/components/QuestionTypes/BaseType.vue index 875326aa..0dbae79c 100644 --- a/src/components/QuestionTypes/BaseType.vue +++ b/src/components/QuestionTypes/BaseType.vue @@ -30,7 +30,8 @@ enterPressed: false, allowedChars: null, alwaysAllowedKeys: ['ArrowLeft', 'ArrowRight', 'Delete', 'Backspace'], - focused: false + focused: false, + canReceiveFocus: false, } }, mounted() { @@ -86,7 +87,7 @@ clearTimeout(this.timeoutId) if ($event) { - if ($event.key === 'Enter') { + if ($event.key === 'Enter' && !$event.shiftKey) { this.unsetFocus() } @@ -156,10 +157,6 @@ } }, computed: { - editingFinished() { - return true - }, - placeholder() { return this.question.placeholder || this.language.placeholder }, diff --git a/src/components/QuestionTypes/LongTextType.vue b/src/components/QuestionTypes/LongTextType.vue index edde6653..2fee8242 100644 --- a/src/components/QuestionTypes/LongTextType.vue +++ b/src/components/QuestionTypes/LongTextType.vue @@ -35,6 +35,11 @@ components: { TextareaAutosize }, + data () { + return { + canReceiveFocus: true + } + }, mounted() { window.addEventListener('resize', this.onResizeListener) }, @@ -63,11 +68,6 @@ this._onEnter() } } - }, - computed: { - editingFinished() { - return !this.isMobile - } } } \ No newline at end of file diff --git a/src/components/QuestionTypes/PhoneType.vue b/src/components/QuestionTypes/PhoneType.vue index fbc2816f..582477ef 100644 --- a/src/components/QuestionTypes/PhoneType.vue +++ b/src/components/QuestionTypes/PhoneType.vue @@ -35,7 +35,8 @@ name: QuestionType.Phone, data() { return { - inputType: 'tel' + inputType: 'tel', + canReceiveFocus: true } } } diff --git a/src/components/QuestionTypes/TextType.vue b/src/components/QuestionTypes/TextType.vue index 615fde05..b170e961 100644 --- a/src/components/QuestionTypes/TextType.vue +++ b/src/components/QuestionTypes/TextType.vue @@ -30,7 +30,8 @@ name: QuestionType.Text, data() { return { - inputType: 'text' + inputType: 'text', + canReceiveFocus: true } } }