DigitalConsumer.ISS/src/common-components/textbox-question/textbox-question.vue

190 lines
4.2 KiB
Vue

<template>
<div class="textbox-question" :class="(errors && errors.length) || hasError ? 'has-error' : ''">
<label :for="inputId" :aria-label="questionText" class="form-label" v-html="labelText"></label>
<input
v-model.trim="value"
v-maska="mask"
:type="type"
class="form-control"
:ref="inputId"
:id="inputId"
:name="inputId"
:placeholder="placeholderText"
:aria-disabled="isDisabled"
:disabled="isDisabled"
:aria-required="isRequired"
:aria-label="questionText"
:class="[hasIcon ? 'has-icon' : '', iconRight ? 'icon-right' : '']"
:validationRules="validationRules"
@change="handleChange"
@blur="handleChange"
:maxlength="maxLength ? maxLength : '999'"
@focus="$emit('focus', $event.target.value)"
/>
<div v-show="errorMessage" class="row my-2 form-test-error">
<span class="d-inline-flex mt-0" role="alert">{{ errorMessage }}</span>
</div>
</div>
</template>
<script>
import { useField, validate } from "vee-validate"
export default {
name: "textbox-question",
props: {
type: {
type: String,
default: "text",
},
placeholderText: {
type: String,
default: "",
},
modelValue: String,
inputId: String,
isDisabled: Boolean,
isRequired: Boolean,
hasIcon: Boolean, // If input has an icon
iconRight: Boolean, // Place icon on right side of text input, otherwise default is left if hasIcon prop is used
hasError: Boolean,
mask: {
type: String,
default: "",
},
validationRules: String,
cmsWidgetName: String,
maxLength: String,
disableAutoFill: Boolean,
},
setup(props) {
const propsClone = Object.assign({}, props);
const modelValue = propsClone.modelValue;
let initialValue;
switch (typeof modelValue) {
case "number":
initialValue = modelValue;
break;
default:
initialValue = (modelValue && modelValue.length > 0) ? modelValue : "";
break;
}
const fieldOptions = {
type: "text",
value: modelValue,
initialValue: initialValue,
};
const { errorMessage, handleBlur, handleChange, meta, validate, errors } =
useField(props.inputId, props.validationRules, fieldOptions);
return {
errorMessage,
handleBlur,
handleChange,
validate,
meta,
errors,
};
},
computed: {
questionText() {
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
},
value: {
get: function () {
return this.modelValue;
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);
},
},
labelText: {
get: function () {
const noBreakChar = "&NoBreak;";
var questionText = "";
if (this.disableAutoFill) {
var words = this.questionText.toString().split(/[ ]+/);
words.forEach(function (word) {
const position = 1;
word = [
word.toString().slice(0, position),
noBreakChar,
word.toString().slice(position),
].join("");
questionText += `${word} `;
});
questionText = questionText.trimEnd();
} else {
questionText = this.questionText.toString();
}
return questionText;
},
},
},
watch: {
async value(newValue) {
const result = await validate(newValue, this.validationRules); // do a test validation check, without triggering full validation
if (result.valid) {
this.handleChange(newValue); // trigger full validation on this field only
}
},
},
};
</script>
<style lang="scss">
.textbox-question {
label {
color: $black;
font-weight: 500;
}
input {
&.has-icon {
background-image: url(~@/assets/img/icons/location-pin.svg);
background-repeat: no-repeat;
background-position: .75rem 50%;
background-size: 1rem auto;
padding: 0 0.75rem 0 2.5rem;
&.icon-right {
background-position: calc(100% - .75rem) 50%;
padding: 0 2.5rem 0 0.75rem
}
}
}
.form-label {
margin-bottom: .25rem;
}
.form-control {
border: 1px solid $gray-500;
border-radius: .5rem;
min-height: 3rem;
padding: 12px 16px;
&::placeholder {
color: $gray-500;
}
&:focus {
box-shadow: 0 0 0 2.5px $blue;
}
&:disabled,
&.disabled {
background-color: $gray-100;
&:hover {
box-shadow: 0 0 0 4px transparent;
border: 1px solid $gray-500;
}
}
&:hover {
border: 1px solid $gray-500;
box-shadow: 0 0 0 4px $blue-300;
}
}
p {
display: none;
}
}
</style>