Skip to content

Commit c7cb015

Browse files
authored
Merge pull request #99 from ditdot-dev/issue.longtextenter
Allow for multiline LongTextType on mobile devices
2 parents 59d85fc + 406c7a5 commit c7cb015

File tree

8 files changed

+94
-35
lines changed

8 files changed

+94
-35
lines changed

src/components/FlowForm.vue

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@
148148
window.addEventListener('beforeunload', this.onBeforeUnload)
149149
150150
this.setQuestions()
151-
this.focusActiveQuestion()
152151
},
153152
beforeDestroy() {
154153
document.removeEventListener('keyup', this.onKeyListener, true)
@@ -394,8 +393,6 @@
394393
395394
if (this.activeQuestionIndex > 0) {
396395
--this.activeQuestionIndex
397-
398-
this.focusActiveQuestion()
399396
}
400397
401398
this.reverse = true
@@ -414,16 +411,6 @@
414411
this.reverse = false
415412
},
416413
417-
focusActiveQuestion() {
418-
this.$nextTick(() => {
419-
const q = this.activeQuestionComponent()
420-
421-
if (q) {
422-
q.focusField()
423-
}
424-
})
425-
},
426-
427414
/**
428415
* Removes focus from the currently focused DOM element.
429416
*/

src/components/Question.vue

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Single question template and logic
22

33
<template>
4-
<div class="animate q-form" v-bind:class="mainClasses">
5-
<div class="q-inner" ref="qinner">
4+
<div class="animate q-form" v-bind:class="mainClasses" ref="qanimate">
5+
<div class="q-inner">
66
<div v-bind:class="{'section-wrap': question.type === QuestionType.SectionBreak}">
77
<div v-bind:class="{'fh2': question.type !== QuestionType.SectionBreak}">
88
<span class="f-title" v-if="question.tagline">{{ question.tagline }}</span>
@@ -31,7 +31,7 @@
3131
<span class="f-sub" v-if="question.subtitle || question.type === QuestionType.LongText || question.multiple">
3232
<span v-if="question.subtitle">{{ question.subtitle }}</span>
3333

34-
<span class="f-help" v-if="question.type === QuestionType.LongText">{{ question.helpText || language.longTextHelpText }}</span>
34+
<span class="f-help" v-if="question.type === QuestionType.LongText && !isMobile">{{ question.helpText || language.longTextHelpText }}</span>
3535

3636
<span class="f-help" v-if="question.multiple">{{ question.helpText || language.multipleChoiceHelpText }}</span>
3737
</span>
@@ -65,7 +65,7 @@
6565
<span v-else>{{ language.ok }}</span>
6666
</div>
6767

68-
<span class="f-enter-desc">{{ language.pressEnter }}</span>
68+
<span class="f-enter-desc" v-if="question.type !== QuestionType.LongText || !isMobile">{{ language.pressEnter }}</span>
6969
</a>
7070

7171
<div v-if="showInvalid()" class="f-invalid" role="alert" aria-live="assertive">{{ language.invalidPrompt }}</div>
@@ -91,6 +91,7 @@
9191
import FlowFormSectionBreakType from './QuestionTypes/SectionBreakType.vue'
9292
import FlowFormTextType from './QuestionTypes/TextType.vue'
9393
import FlowFormUrlType from './QuestionTypes/UrlType.vue'
94+
import { IsMobile } from '../mixins/IsMobile'
9495
9596
export default {
9697
name: 'FlowFormQuestion',
@@ -119,6 +120,9 @@
119120
default: false
120121
}
121122
},
123+
mixins: [
124+
IsMobile
125+
],
122126
data() {
123127
return {
124128
QuestionType: QuestionType,
@@ -129,33 +133,35 @@
129133
this.focusField()
130134
this.dataValue = this.question.answer
131135
132-
this.$refs.qinner.addEventListener('transitionend', this.onTransitionEnd)
136+
this.$refs.qanimate.addEventListener('animationend', this.onAnimationEnd)
133137
},
134138
beforeDestroy() {
135-
this.$refs.qinner.removeEventListener('transitionend', this.onTransitionEnd)
139+
this.$refs.qanimate.removeEventListener('animationend', this.onAnimationEnd)
136140
},
137141
methods: {
138142
/**
139143
* Autofocus the input box of the current question
140144
*/
141145
focusField() {
142-
let el = this.$refs.questionComponent
146+
const el = this.$refs.questionComponent
143147
144148
el && el.focus()
145149
},
146150
147-
onTransitionEnd() {
148-
this.enterPressed = false
151+
onAnimationEnd() {
152+
this.focusField()
149153
},
150154
151155
/**
152156
* Emits "answer" event and calls "onEnter" method on Enter press
153157
*/
154-
onEnter() {
158+
onEnter($event) {
155159
const q = this.$refs.questionComponent
156160
157161
if (q) {
158-
this.$emit('answer', q)
162+
if (!q.focused) {
163+
this.$emit('answer', q)
164+
}
159165
q.onEnter()
160166
}
161167
},

src/components/QuestionTypes/BaseType.vue

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
import QuestionModel, { QuestionType } from '../../models/QuestionModel'
1010
import LanguageModel from '../../models/LanguageModel'
11+
import { IsMobile } from '../../mixins/IsMobile'
1112
1213
export default {
1314
name: 'FlowFormBaseType',
@@ -17,14 +18,18 @@
1718
active: Boolean,
1819
value: [String, Array]
1920
},
21+
mixins: [
22+
IsMobile
23+
],
2024
data() {
2125
return {
2226
dirty: false,
2327
dataValue: '',
2428
answer: null,
2529
enterPressed: false,
2630
allowedChars: null,
27-
alwaysAllowedKeys: ['ArrowLeft', 'ArrowRight', 'Delete', 'Backspace']
31+
alwaysAllowedKeys: ['ArrowLeft', 'ArrowRight', 'Delete', 'Backspace'],
32+
focused: false
2833
}
2934
},
3035
mounted() {
@@ -52,6 +57,15 @@
5257
return el
5358
},
5459
60+
setFocus() {
61+
this.focused = true
62+
},
63+
64+
// eslint-disable-next-line no-unused-vars
65+
unsetFocus($event) {
66+
this.focused = false
67+
},
68+
5569
focus() {
5670
const el = this.getElement()
5771
@@ -68,11 +82,17 @@
6882
this.enterPressed = false
6983
clearTimeout(this.timeoutId)
7084
71-
if ($event && this.allowedChars !== null) {
72-
// Check if the entered character is allowed.
73-
// We always allow keys from the alwaysAllowedKeys array.
74-
if (this.alwaysAllowedKeys.indexOf($event.key) === -1 && this.allowedChars.indexOf($event.key) === -1) {
75-
$event.preventDefault()
85+
if ($event) {
86+
if ($event.key === 'Enter') {
87+
this.unsetFocus()
88+
}
89+
90+
if (this.allowedChars !== null) {
91+
// Check if the entered character is allowed.
92+
// We always allow keys from the alwaysAllowedKeys array.
93+
if (this.alwaysAllowedKeys.indexOf($event.key) === -1 && this.allowedChars.indexOf($event.key) === -1) {
94+
$event.preventDefault()
95+
}
7696
}
7797
}
7898
},
@@ -86,11 +106,11 @@
86106
},
87107
88108
onEnter() {
89-
this.enterPressed = true
109+
this._onEnter()
110+
},
90111
91-
if (this.question.type === QuestionType.SectionBreak) {
92-
this.dirty = true
93-
}
112+
_onEnter() {
113+
this.enterPressed = true
94114
95115
this.dataValue = this.fixAnswer(this.dataValue)
96116
this.setAnswer(this.dataValue)
@@ -133,6 +153,10 @@
133153
}
134154
},
135155
computed: {
156+
editingFinished() {
157+
return true
158+
},
159+
136160
placeholder() {
137161
return this.question.placeholder || this.language.placeholder
138162
},

src/components/QuestionTypes/LongTextType.vue

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
v-bind:required="question.required"
88
v-on:keydown.native="onKeyDown"
99
v-on:keyup.native="onChange"
10-
v-on:keydown.enter.exact.prevent.native
10+
v-on:keydown.enter.exact.native="onEnterDown"
1111
v-on:keyup.enter.exact.prevent.native="onEnter"
1212
v-on:keyup.tab.prevent.native="onEnter"
13+
v-on:focus.native="setFocus"
14+
v-on:blur.native="unsetFocus"
1315
v-bind:placeholder="placeholder"
1416
/>
1517
</span>
@@ -41,6 +43,29 @@
4143
methods: {
4244
onResizeListener() {
4345
this.$refs.input.resize()
46+
},
47+
48+
unsetFocus($event) {
49+
if ($event || !this.isMobile) {
50+
this.focused = false
51+
}
52+
},
53+
54+
onEnterDown($event) {
55+
if (!this.isMobile) {
56+
$event.preventDefault()
57+
}
58+
},
59+
60+
onEnter() {
61+
if (!this.isMobile) {
62+
this._onEnter()
63+
}
64+
}
65+
},
66+
computed: {
67+
editingFinished() {
68+
return !this.isMobile
4469
}
4570
}
4671
}

src/components/QuestionTypes/PhoneType.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
v-bind:placeholder="placeholder"
99
v-on:keydown.native="onKeyDown"
1010
v-on:keyup.native="onChange"
11+
v-on:focus.native="setFocus"
12+
v-on:blur.native="unsetFocus"
1113
v-on:keyup.native.enter.prevent="onEnter"
1214
v-on:keyup.native.tab.prevent="onEnter"
1315
/>

src/components/QuestionTypes/SectionBreakType.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
extends: BaseType,
1919
name: QuestionType.SectionBreak,
2020
methods: {
21+
onEnter() {
22+
this.dirty = true
23+
24+
this._onEnter()
25+
},
26+
2127
isValid() {
2228
return true
2329
}

src/components/QuestionTypes/TextType.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
v-on:keyup="onChange"
99
v-on:keyup.enter.prevent="onEnter"
1010
v-on:keyup.tab.prevent="onEnter"
11+
v-on:focus="setFocus"
12+
v-on:blur="unsetFocus"
1113
v-bind:placeholder="placeholder"
1214
/>
1315
</template>

src/mixins/IsMobile.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const IsMobile = {
2+
data() {
3+
return {
4+
isMobile: !!navigator.userAgent.match(/android|iphone|ipad|ipod/i)
5+
}
6+
}
7+
}

0 commit comments

Comments
 (0)