# ZeenSurveyModal
Компонент опроса
# Example
# Slots
Название | Описание | Обязательный | По умолчанию |
---|---|---|---|
startHeader | Верхний блок стартового окна | - | - |
endHeader | Верхний блок окна результата | - | - |
subTitle | Подзаголовок (принимает объект #subTitle='{question}' для фильтрации по типу вопроса question.type: rates, multiple_rates, checkboxes, radio, input) | - | - |
# Props
Название | Тип | Обязательный | По умолчанию | Описание |
---|---|---|---|---|
survey | Object | + | Данные опроса | |
name | Boolean | - | 'survey-modal' | Имя модалки голосования |
buttonText | Object | - | { start: 'Начать', next: 'Далее', finally: 'Закрыть', } | Объект с текстами кнопок |
rateText | Object | - | { low: 'плохо', high: 'хорошо', } | Объект с текстами описания границ рейтинга |
maxRate | Number | - | 5 | Максимальная оценка (рейтинг) |
resultTitle | String | - | 'Спасибо за обратную связь!' | Текст результата |
textAreaPlaceholder | String | - | Пустая строка | Текст плейсхолдера для полей ввода |
isComplete | Boolean | - | false | Пройден ли опрос юзером |
# Source Code
<template lang="pug">
ZeenModal(:name='name' size='large' v-on='modalEvents')
.zeen-survey
.zeen-survey__wrapper(v-if='isFirstStep')
.zeen-survey__header
slot(name='startHeader')
ZeenHeadline.zeen-survey__main-title(tag='h4' v-html='surveyData.title')
.zeen-survey__btn
ZeenButton(
@click.prevent='next'
:disabled='isButtonDisabled'
) {{ buttonText.start }}
.zeen-survey__wrapper-list(v-else-if='isQuestionStep')
.zeen-survey__wrapper
.zeen-survey__top
.zeen-survey__step-number {{step}}/{{surveyData.survey_questions.length}}
ZeenHeadline(tag='h3') {{question.title}}
.zeen-survey__sub-title
slot(name='subTitle' :question='question')
//- rates
.zeen-survey__rates-wrapper(v-if='isQuestionTypeRates')
.zeen-survey__rates
.zeen-survey__rate(
v-for='rate in rates'
:key='rate'
:class='getRatesClass(rate)'
@click.prevent='setRateAnswer(question, rate)'
) {{rate}}
.zeen-survey__rates-description
p {{rateText.low}}
p {{rateText.high}}
//- multiple_rates
.zeen-survey__multiple-rates-wrapper(v-else-if='isQuestionTypeMultipleRates')
.zeen-survey__multiple-rates(v-for='item in question.survey_answers' :key='item.id')
p.zeen-survey__multiple-rates-title(v-html='item.title')
.zeen-survey__multiple-rates-inner
.zeen-survey__multiple-rate(
v-for='rate in rates'
:key='rate'
:class='{"zeen-survey__multiple-rate--active": checkMultipleRateActive(item.id, rate)}'
@click.prevent='setMultipleRateAnswer(question, item.id, rate)'
) {{rate}}
//- checkboxes
.zeen-survey__answers(v-else-if='isQuestionTypeCheckboxes')
.zeen-survey__answer(v-for='answer in question.survey_answers' :key='answer.id')
ZeenCheckbox(v-model='answer.value' :label='answer.title' :value='answer.value' @change='checkCheckboxValidation(question.survey_answers)')
//- radio
.zeen-survey__answers(v-else-if='isQuestionTypeRadio')
.zeen-survey__answer(v-for='answer in question.survey_answers' :key='answer.id')
ZeenRadio(v-model='activeAnswer' :value='answer.id' :name='question.id' :label='answer.title' @change='setRadioAnswer(question.id, answer.id, answer.title)')
//- input
.zeen-survey__answers(v-else-if='isQuestionTypeInput')
.zeen-survey__answer
ZeenTextArea(:placeholder="textAreaPlaceholder" :value='activeAnswerInput' @input='setInputAnswer')
.zeen-survey__btn
ZeenButton(
:disabled='isButtonDisabled'
@click.prevent='nextQuestion(question)'
) {{ buttonText.next }}
//- results
.zeen-survey__wrapper(v-else)
.zeen-survey__header.zeen-survey__header--end
slot(name='endHeader')
ZeenHeadline.zeen-survey__main-title(tag='h3' v-html='resultTitle')
.zeen-survey__btn
ZeenButton(
@click.prevent='closeModal'
) {{ buttonText.finally }}
</template>
<script>
import ZeenHeadline from '../ZeenHeadline'
import ZeenButton from '../ZeenButton'
import ZeenModal from '../ZeenModal'
import {createEventsFor} from '../../helpers/createBlockData'
export default {
name: 'ZeenSurvey',
components: {
ZeenHeadline,
ZeenButton,
ZeenModal,
},
props: {
name: {
type: String,
default: 'survey-modal',
},
survey: {
type: Object,
default: () => {
return {}
},
},
buttonText: {
type: Object,
default: () => {
return {
start: 'Начать',
next: 'Далее',
finally: 'Закрыть',
}
},
},
rateText: {
type: Object,
default: () => {
return {
low: 'плохо',
high: 'хорошо',
}
},
},
maxRate: {
type: Number,
default: 5,
},
resultTitle: {
type: String,
default: 'Спасибо за обратную связь!',
},
textAreaPlaceholder: {
type: String,
default: '',
},
isComplete: {
type: Boolean,
default: false,
},
},
data() {
return {
isButtonDisabled: false,
step: 0,
surveyResults: [],
activeRateValue: 0,
// Переменные для хранения временных результатов разных типов
activeAnswer: {},
activeAnswerMultiple: [],
activeAnswerInput: '',
}
},
methods: {
next() {
this.step++
this.isButtonDisabled = true
},
nextQuestion(question) {
this.saveResult(question)
if (this.step === this.surveyData.survey_questions.length) {
this.saveData()
}
this.next()
},
saveResult(question) {
switch (question.type) {
case 'checkboxes': {
question.survey_answers.forEach((item) => {
if (item.value) {
this.addResult(question.id, item.id, item.title)
}
})
break
}
case 'input': {
this.addResult(question.id, question.survey_answers[0].id, this.activeAnswerInput)
this.activeAnswerInput = ''
break
}
case 'multiple_rates': {
this.surveyResults = this.surveyResults.concat(this.activeAnswerMultiple)
this.activeAnswerMultiple = []
break
}
default: {
this.surveyResults.push(this.activeAnswer)
this.activeAnswer = {}
this.activeRateValue = 0
}
}
},
addResult(questionId, answerId, value) {
this.surveyResults.push({
survey_question_id: questionId,
survey_answer_id: answerId,
answer: value,
})
},
checkCheckboxValidation(answers) {
this.isButtonDisabled = true
answers.forEach((item) => {
if (item.value === true) {
this.isButtonDisabled = false
}
})
},
getQuestionType(question) {
const answer = question.multiple_answer
const type = question.survey_answers[0].answer_type
const isRates = !answer && type === 'rating'
const isMultipleRates = answer && type === 'rating'
const isCheckboxes = answer && type === 'boolean'
const isRadio = !answer && type === 'boolean'
const isInput = !answer && type === 'input'
switch (true) {
case isRates: {
return 'rates'
}
case isMultipleRates: {
return 'multiple_rates'
}
case isCheckboxes: {
question.survey_answers.forEach((item) => {
item.value = false
})
return 'checkboxes'
}
case isRadio: {
return 'radio'
}
case isInput: {
return 'input'
}
default: {
return 'error'
}
}
},
getRatesClass(rate) {
return this.activeRateValue >= rate ? 'zeen-survey__rate--active' : ''
},
setRateAnswer(question, rate) {
this.activeRateValue = rate
this.activeAnswer = {
survey_question_id: question.id,
survey_answer_id: question.survey_answers[0].id,
answer: rate,
}
this.isButtonDisabled = false
},
setMultipleRateAnswer(question, answerId, rate) {
const result = {
survey_question_id: question.id,
survey_answer_id: answerId,
answer: rate,
}
const resultInArray = this.activeAnswerMultiple.find((item) => item.survey_answer_id === result.survey_answer_id)
if (resultInArray) {
this.activeAnswerMultiple.splice(this.activeAnswerMultiple.indexOf(resultInArray), 1)
}
this.activeAnswerMultiple.push(result)
this.activeAnswerMultiple = [...this.activeAnswerMultiple]
if (question.survey_answers.length === this.activeAnswerMultiple.length) {
this.isButtonDisabled = false
}
},
setInputAnswer(value) {
this.activeAnswerInput = value
if (this.activeAnswerInput.trim()) {
this.isButtonDisabled = false
} else {
this.isButtonDisabled = true
}
},
setRadioAnswer(questionId, answerId, value) {
this.activeAnswer = {
survey_question_id: questionId,
survey_answer_id: answerId,
answer: value,
}
this.isButtonDisabled = false
},
saveData() {
this.$emit('send', this.surveyResults)
},
closeModal() {
this.$vfm.hide(this.name)
},
checkMultipleRateActive(id, rate) {
return this.activeAnswerMultiple.find((item) => item.survey_answer_id === id)?.answer >= rate
},
},
computed: {
modalEvents() {
return createEventsFor(this, 'modal_')
},
surveyData() {
const surveyData = JSON.parse(JSON.stringify(this.survey))
surveyData?.survey_questions?.forEach((item) => {
item.type = this.getQuestionType(item)
})
return surveyData
},
rates() {
let rates = [...Array(this.maxRate + 1).keys()]
rates.shift()
return rates
},
question() {
return this.surveyData.survey_questions[this.step - 1]
},
isFirstStep() {
return !this.isComplete && this.step === 0
},
isQuestionStep() {
return !this.isComplete && this.step <= this.surveyData?.survey_questions?.length
},
isQuestionTypeRates() {
return this.question.type === `rates`
},
isQuestionTypeMultipleRates() {
return this.question.type === `multiple_rates`
},
isQuestionTypeCheckboxes() {
return this.question.type === `checkboxes`
},
isQuestionTypeRadio() {
return this.question.type === `radio`
},
isQuestionTypeInput() {
return this.question.type === `input`
},
},
watch: {},
}
</script>
<style lang="scss">
@import '/src/styles/mixins.scss';
:root {
/* Размеры */
--zeen-survey-rate-size: 40px;
--zeen-survey-rate-gap: 20px;
@include phones {
--zeen-survey-rate-size: 30px;
--zeen-survey-rate-gap: 10px;
}
/* Цвета */
--zeen-survey-main-color: var(--main-light);
--zeen-survey-top-color: var(--gray-1);
--zeen-survey-rate-background: var(--gray-3);
--zeen-survey-rate-active-background: var(--main-color);
--zeen-survey-rate-color: var(--input-main-value-color);
--zeen-survey-rate-active-color: var(--main-light);
--zeen-survey-step-number-color: var(--gray-4);
--zeen-survey-rates-descroption-color: var(--input-main-value-color);
}
</style>
<style lang="scss" scoped>
@import '../../styles/mixins.scss';
* {
margin: 0;
padding: 0;
}
::v-deep {
.modal__content {
padding: 0;
overflow: hidden;
}
.modal__description {
margin: 0;
}
}
.zeen-survey {
&__main-title,
&__top {
padding: 50px 50px 30px;
background: var(--zeen-survey-top-color);
@include phones-small {
padding: 40px 30px 30px;
}
}
&__main-title {
background: transparent;
}
&__btn {
margin-left: 50px;
margin-bottom: 50px;
@include phones-small {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
margin-bottom: 30px;
}
}
&__step-number {
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 22px;
display: flex;
align-items: center;
color: var(--zeen-survey-step-number-color);
margin-bottom: 10px;
}
&__header {
display: flex;
justify-content: center;
align-items: center;
background: var(--zeen-survey-top-color);
}
&__answers {
padding: 30px 50px 10px;
@include phones-small {
padding: 30px 30px 10px;
}
}
&__answer {
margin-bottom: 20px;
}
// rates
&__rates-wrapper {
width: fit-content;
}
&__rates {
display: flex;
align-items: center;
width: fit-content;
padding: 40px 50px 10px;
flex-wrap: wrap;
@include phones-small {
padding: 40px 30px 10px;
}
}
&__rate {
display: flex;
justify-content: center;
align-items: center;
width: var(--zeen-survey-rate-size);
height: var(--zeen-survey-rate-size);
background: var(--zeen-survey-rate-background);
color: var(--zeen-survey-rate-color);
border-radius: 50%;
cursor: pointer;
margin-right: var(--zeen-survey-rate-gap);
&--active {
color: var(--zeen-survey-rate-active-color);
background: var(--zeen-survey-rate-active-background);
}
&:last-child {
margin-right: 0;
}
@include phones-small {
&:last-child {
margin-right: 0;
}
}
}
&__rates-description {
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 24px;
display: flex;
justify-content: space-between;
margin-bottom: 40px;
padding: 0 50px;
p {
color: var(--zeen-survey-rates-descroption-color);
}
@include phones-small {
width: 100%;
padding: 0 30px;
}
}
// multiple-rates
&__multiple-rates-wrapper {
padding: 40px 50px 30px;
@include phones-small {
padding: 40px 30px 30px;
}
}
&__multiple-rates {
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
@include phones-small {
flex-direction: column;
}
}
&__multiple-rates-title {
@include phones-small {
margin-bottom: 5px;
}
}
&__multiple-rates-inner {
display: flex;
align-items: center;
width: fit-content;
}
&__multiple-rate {
display: flex;
justify-content: center;
align-items: center;
width: var(--zeen-survey-rate-size);
height: var(--zeen-survey-rate-size);
background: var(--zeen-survey-rate-background);
color: var(--zeen-survey-rate-color);
border-radius: 50%;
cursor: pointer;
margin-right: var(--zeen-survey-rate-gap);
&--active {
color: var(--zeen-survey-rate-active-color);
background: var(--zeen-survey-rate-active-background);
}
&:last-child {
margin-right: 0;
}
@include phones-small {
&:last-child {
margin-right: 0;
}
}
}
}
</style>