Skip to content

Commit 2ace81e

Browse files
committed
Add basic opinion scale question
1 parent 0a6d282 commit 2ace81e

File tree

5 files changed

+251
-4
lines changed

5 files changed

+251
-4
lines changed

examples/questionnaire/Example.vue

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,40 @@
8989
language: new LanguageModel(),
9090
// Create question list with QuestionModel instances
9191
questions: [
92+
new QuestionModel({
93+
id: 'opinion_scale_num',
94+
tagline: 'Hi! Welcome to our demo survey 😊',
95+
title: 'Tell us about our service',
96+
subtitle: '1 - Awful, 5 - So, so, 10 - Excellent',
97+
type: QuestionType.OpinionScale,
98+
required: true,
99+
min: 1,
100+
max: 10,
101+
}),
102+
new QuestionModel({
103+
id: 'opinion_scale_text',
104+
tagline: 'Hi! Welcome to our demo survey 😊',
105+
title: 'Tell us about our service',
106+
type: QuestionType.OpinionScale,
107+
required: true,
108+
options: [
109+
new ChoiceOption({
110+
label: 'Awful'
111+
}),
112+
new ChoiceOption({
113+
label: 'Poor'
114+
}),
115+
new ChoiceOption({
116+
label: 'Just OK'
117+
}),
118+
new ChoiceOption({
119+
label: 'Very good'
120+
}),
121+
new ChoiceOption({
122+
label: 'Excellent'
123+
})
124+
]
125+
}),
92126
new QuestionModel({
93127
id: 'first_name',
94128
tagline: 'Hi! Welcome to our demo survey 😊',

src/assets/css/common.css

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,54 @@ header.vff-header svg.f-logo {
739739
max-width: 100%;
740740
}
741741

742+
/*
743+
field opinion scale
744+
*/
745+
746+
.vff .field-opinionscale ul.f-radios {
747+
width: 100%;
748+
max-width: 750px;
749+
min-width: auto;
750+
display: -ms-flexbox;
751+
display: -webkit-box;
752+
display: flex;
753+
margin: 0 -8px 0 0;
754+
-webkit-box-orient: vertical;
755+
-webkit-box-direction: normal;
756+
-ms-flex-flow: 0 1;
757+
-webkit-box-align: stretch;
758+
-ms-flex-align: stretch;
759+
align-items: stretch;
760+
-ms-flex-wrap: wrap;
761+
flex-wrap: wrap;
762+
}
763+
764+
.vff .field-opinionscale ul.f-radios li {
765+
display: -ms-flexbox;
766+
display: -webkit-box;
767+
display: flex;
768+
-webkit-box-align: center;
769+
align-items: center;
770+
margin-bottom: 8px;
771+
position: relative;
772+
cursor: pointer;
773+
-ms-flex-align: center;
774+
-webkit-box-orient: vertical;
775+
-webkit-box-direction: normal;
776+
padding: 8px 8px 10px;
777+
margin-right: 8px;
778+
-ms-flex: 0 0 calc(20% - 8px);
779+
flex: 0 0 calc(20% - 8px);
780+
font-size: 15px;
781+
line-height: 1.38;
782+
height: 64px;
783+
}
784+
785+
.vff .field-opinionscale ul.f-radios.f-numbers li {
786+
-ms-flex: 0 0 calc(10% - 8px);
787+
flex: 0 0 calc(10% - 8px);
788+
}
789+
742790
/*
743791
field matrix
744792
*/
@@ -1186,14 +1234,21 @@ header.vff-header svg.f-logo {
11861234
font-size: 16px;
11871235
}
11881236

1189-
.vff .field-multiplepicturechoice ul.f-radios {
1237+
.vff .field-multiplepicturechoice ul.f-radios,
1238+
.vff .field-opinionscale ul.f-radios {
11901239
max-width: 320px;
11911240
}
11921241

1193-
.vff .field-multiplepicturechoice ul.f-radios li {
1242+
.vff .field-multiplepicturechoice ul.f-radios li,
1243+
.vff .field-opinionscale ul.f-radios li {
11941244
-ms-flex: 0 0 calc(50% - 8px);
11951245
flex: 0 0 calc(50% - 8px);
11961246
}
1247+
1248+
.vff .field-opinionscale ul.f-radios.f-numbers li {
1249+
-ms-flex: 0 0 calc(20% - 8px);
1250+
flex: 0 0 calc(20% - 8px);
1251+
}
11971252
}
11981253

11991254
@media screen and (max-height:400px) {

src/components/FlowFormQuestion.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
import FlowFormDateType from './QuestionTypes/DateType.vue'
115115
import FlowFormFileType from './QuestionTypes/FileType.vue'
116116
import FlowFormMatrixType from './QuestionTypes/MatrixType.vue'
117+
import FlowFormOpinionScaleType from './QuestionTypes/OpinionScaleType.vue'
117118
import { IsMobile } from '../mixins/IsMobile'
118119
119120
@@ -134,7 +135,8 @@
134135
FlowFormTextType,
135136
FlowFormFileType,
136137
FlowFormUrlType,
137-
FlowFormMatrixType
138+
FlowFormMatrixType,
139+
FlowFormOpinionScaleType,
138140
},
139141
140142
props: {
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<template>
2+
<div class="f-radios-wrap">
3+
<ul class="f-radios" v-bind:class="{'f-numbers': question.max}" role="listbox">
4+
<li
5+
v-for="(option, index) in question.options"
6+
v-on:click.prevent="toggleAnswer(option)"
7+
v-bind:class="{'f-selected': option.selected}"
8+
v-bind:key="'m' + index"
9+
v-bind:aria-label="getLabel(index)"
10+
role="option"
11+
>
12+
<div class="f-label-wrap">
13+
<span class="f-key">{{ getToggleKey(index) }}</span>
14+
<span v-if="option.choiceLabel()" class="f-label">{{ option.choiceLabel() }}</span>
15+
</div>
16+
</li>
17+
</ul>
18+
</div>
19+
</template>
20+
21+
<script>
22+
/*
23+
Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence
24+
https://github.com/ditdot-dev/vue-flow-form
25+
https://www.ditdot.hr/en
26+
*/
27+
28+
import BaseType from './BaseType.vue'
29+
import { ChoiceOption, QuestionType } from '../../models/QuestionModel'
30+
31+
export default {
32+
extends: BaseType,
33+
name: QuestionType.OpinionScale,
34+
35+
beforeMount() {
36+
if (this.question.max && !this.question.options.length) {
37+
const
38+
min = this.question.min || 1,
39+
max = this.question.max
40+
for (let i = min; i <= max; i++) {
41+
this.question.options.push(new ChoiceOption({value: i.toString()}))
42+
}
43+
}
44+
},
45+
46+
mounted() {
47+
this.addKeyListener()
48+
},
49+
50+
beforeUnmount() {
51+
this.removeKeyListener()
52+
},
53+
54+
watch: {
55+
active(value) {
56+
if (value) {
57+
this.addKeyListener()
58+
} else {
59+
this.removeKeyListener()
60+
}
61+
}
62+
},
63+
64+
methods: {
65+
addKeyListener() {
66+
this.removeKeyListener()
67+
document.addEventListener('keyup', this.onKeyListener)
68+
},
69+
70+
removeKeyListener() {
71+
document.removeEventListener('keyup', this.onKeyListener)
72+
},
73+
74+
/**
75+
* Listens for keyboard events (A, B, C, ...)
76+
*/
77+
onKeyListener($event) {
78+
if (this.active && $event.key && $event.key.length === 1) {
79+
let keyCode = $event.key.toUpperCase().charCodeAt(0)
80+
81+
if (keyCode >= 65 && keyCode <= 90) {
82+
let index = keyCode - 65
83+
84+
if (index > -1) {
85+
let option = this.question.options[index]
86+
87+
if (option) {
88+
this.toggleAnswer(option)
89+
}
90+
}
91+
}
92+
}
93+
},
94+
95+
getLabel(index) {
96+
return this.language.ariaMultipleChoice.replace(':letter', this.getToggleKey(index))
97+
},
98+
99+
getToggleKey(index) {
100+
const key = 65 + index
101+
102+
if (key <= 90) {
103+
return String.fromCharCode(key)
104+
}
105+
106+
return ''
107+
},
108+
109+
toggleAnswer(option) {
110+
for (let i = 0; i < this.question.options.length; i++) {
111+
let o = this.question.options[i]
112+
113+
if (o.selected) {
114+
this._toggleAnswer(o)
115+
}
116+
}
117+
118+
this._toggleAnswer(option)
119+
},
120+
121+
_toggleAnswer(option) {
122+
const optionValue = option.choiceValue()
123+
console.log(option, optionValue)
124+
125+
option.toggle()
126+
127+
this.dataValue = option.selected ? optionValue : null
128+
129+
if (this.isValid() && this.question.nextStepOnAnswer && !this.disabled) {
130+
this.$emit('next')
131+
}
132+
133+
this.setAnswer(this.dataValue)
134+
},
135+
136+
_removeAnswer(value) {
137+
const index = this.dataValue.indexOf(value)
138+
139+
if (index !== -1) {
140+
this.dataValue.splice(index, 1)
141+
}
142+
},
143+
},
144+
145+
computed: {
146+
hasValue() {
147+
if (this.question.options.filter(o => o.selected).length) {
148+
return true
149+
}
150+
151+
return false
152+
}
153+
}
154+
}
155+
</script>

src/models/QuestionModel.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export const QuestionType = Object.freeze({
2020
SectionBreak: 'FlowFormSectionBreakType',
2121
Text: 'FlowFormTextType',
2222
Url: 'FlowFormUrlType',
23-
Matrix: 'FlowFormMatrixType'
23+
Matrix: 'FlowFormMatrixType',
24+
OpinionScale: 'FlowFormOpinionScaleType'
2425
})
2526

2627
export const DropdownOptionBlank = Object.freeze({

0 commit comments

Comments
 (0)