DigitalConsumer.ISS/src/digital-components/textarea-question/textarea-question.vue
SujathaAnishetty fdecbf0d08 adding meta
2026-01-07 15:47:04 -05:00

191 lines
4.9 KiB
Vue

<template>
<div
class="textarea-question"
:class="[
(errors && errors.length) ? 'has-error' : '',
(meta?.valid && meta?.touched && !(errors && errors.length)) ? 'has-success' : ''
]">
<label
:for="inputId"
class="form-label">
{{ questionText }}
<span
v-if="!isRequired"
class="optional">(Optional)</span>
</label>
<div class="input-wrapper">
<textarea
:id="inputId"
:ref="inputId"
v-model.trim="value"
class="form-control"
:name="inputId"
:rows="[inputRows ?? 10]"
:placeholder="placeholderText"
:disabled="isDisabled"
:required="isRequired"
:maxlength="maxLength"
:validationRules="validationRules"
@change="validationRules ? handleChange : () => {}"
@blur="validationRules ? handleBlur : () => {}"
@paste="trimOnPaste"
@drop="trimOnPaste">
</textarea>
</div>
<p class="character-count margin-top-8">
{{ maxLength - characterCount }}/{{ maxLength }} characters remaining
</p>
<div
v-show="errorMessage"
id="error-message"
class="row my-1 form-test-error">
<span
class="d-inline-flex mt-0"
role="alert">{{ errorMessage }}
</span>
</div>
</div>
</template>
<script>
import { useField } from 'vee-validate';
export default {
name: 'textarea-question',
props: {
placeholderText: {
type: String,
default: ''
},
inputId: {
type: String,
required: true
},
modelValue: String,
isDisabled: Boolean,
isRequired: Boolean,
validationRules: String,
cmsWidgetName: {
type: String,
required: true
},
maxLength: {
type: String,
default: '300'
},
inputRows: Number
},
emits: ['update:modelValue'],
setup(props) {
const propsClone = { ...props };
const { modelValue } = propsClone;
const initialValue = modelValue ?? '';
const fieldOptions = {
type: 'text',
value: modelValue,
initialValue
};
const { errorMessage, handleChange, handleBlur, validate, errors, resetField, meta } =
useField(props.inputId, props.validationRules, fieldOptions);
return {
errorMessage,
handleChange,
handleBlur,
validate,
errors,
resetField,
meta
};
},
computed: {
/**
* @summary Returns the number of characters in the textarea field.
* @returns {number}
*/
characterCount() {
return this?.modelValue?.length ?? 0;
},
/**
* @summary Returns the CMS text associated with the question.
* @returns {string} QuestionText from cms data.
*/
questionText() {
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
value: {
get() {
return this.modelValue;
},
set(newValue) {
this.$emit('update:modelValue', newValue);
}
}
},
methods: {
/**
* @summary Removes whitespace from the end of pasted content.
* @param {event} evt - Browser event
*/
trimOnPaste(evt) {
evt.stopPropagation();
evt.preventDefault();
const data = evt.type === 'paste' ? (evt.clipboardData || window.clipboardData) : evt.dataTransfer;
const value = data.getData('Text')?.trim();
this.$emit('update:modelValue', this.modelValue + value);
}
}
};
</script>
<style lang="scss" scoped>
.textarea-question {
label {
color: $black;
font-weight: 600;
}
.form-test-error span{
color: $red;
font-size: 0.875rem;
font-weight: 500;
}
.form-control {
max-height: 30rem;
border: $border-input;
border-radius: $border-radius;
padding: 12px 16px;
letter-spacing: inherit;
box-shadow: $box-shadow-input;
&::placeholder {
color: $gray-500;
}
&:focus {
border: $border-input-focus;
}
&:disabled,
&.disabled {
background-color: $gray-100;
}
}
}
.optional {
color: $gray-550;
}
.character-count {
color: $gray-600;
font-size: 12px;
margin-bottom: 0px;
}
.margin-top-8 {
margin-top: 8px;
}
</style>