DigitalConsumer.ISS/src/digital-components/base-input-button/base-input-button.vue
2023-08-14 08:08:53 -04:00

173 lines
5.2 KiB
Vue

<template>
<label
:class="[buttonWrapperClasses, { 'has-error': errors.length > 0 &&!suppressError,
selected: isChecked }]"
:for="buttonId"
@focusin="handleFocus"
@focusout="handleBlur"
@mousedown.left="handleEventAction(eventTypes.CLICK, $event)">
<input
:id="buttonId"
:key="buttonId"
:type="inputType"
:name="groupName"
:class="inputClasses"
:aria-required="isRequired"
:value="value"
:checked="isChecked"
@keypress.space="handleEventAction(eventTypes.SPACE, $event)"
@keypress.enter="handleEventAction(eventTypes.ENTER, $event)"
@change="handleEventAction(eventTypes.CHANGE, $event)" />
<slot></slot>
</label>
</template>
<script>
import { useField } from 'vee-validate';
import { toRef } from 'vue';
import {
handleButtonComponentFocus,
handleInputComponentBlur
} from '@/helpers/button-question-focus-helper';
import inputButtonProps from '@/digital-components/base-input-button/button-functionality-props';
export default {
name: 'base-input-button',
props: {
...inputButtonProps,
buttonWrapperClasses: [String, Array, Object],
inputClasses: [String, Array, Object]
},
emits: ['update:modelValue'],
setup(props) {
const inputType = props.isMultiSelect ? 'checkbox' : 'radio';
const fieldOptions = {
type: inputType,
validateOnValueUpdate: false,
validateOnMount: false
};
const { handleChange, meta, errors }
= useField(toRef(props, 'groupName'),
toRef(props, 'validationRules'),
fieldOptions);
return {
handleChange,
errors,
meta,
fieldOptions // only need to expose this for unit test purposes
};
},
data() {
return {
valueToEmit: null
};
},
computed: {
isChecked() {
if (this.isMultiSelect && this.modelValue instanceof Array) {
return this.modelValue.includes(this.value);
}
if (!this.isMultiSelect) {
return this.modelValue === this.value;
}
return false;
},
inputType() {
return this.isMultiSelect ? 'checkbox' : 'radio';
},
buttonId() {
return `${this.groupName}-${this.value}`.replaceAll(' ', '-');
},
isValueSelectedOnClick() {
return this.isMultiSelect || this.selectingInitiatesLoad;
},
eventTypes() {
return {
CHANGE: 'change',
ENTER: 'enter',
SPACE: 'space',
CLICK: 'click'
};
}
},
mounted() {
if (this.isChecked) {
this.handleChange(this.modelValue);
}
},
methods: {
handleEventAction(eventType, e) {
if (this.isMultiSelect) {
switch (eventType) {
case this.eventTypes.CHANGE:
this.handleClick(e);
break;
default:
}
} else {
switch (eventType) {
case this.eventTypes.CLICK:
case this.eventTypes.ENTER:
case this.eventTypes.SPACE:
this.handleClick(e);
break;
case this.eventTypes.CHANGE:
// eslint-disable-next-line no-unused-expressions
this.selectingInitiatesLoad
? this.handleSelectionChange(e)
: this.handleClick(e);
break;
default:
}
}
},
handleSelectionChange() {
if (
this.isMultiSelect
&& (this.modelValue instanceof Array || this.modelValue == null)
) {
const newValue = this.modelValue ? [...this.modelValue] : [];
if (!newValue.includes(this.value)) {
newValue.push(this.value);
} else {
newValue.splice(newValue.indexOf(this.value), 1);
}
this.valueToEmit = newValue;
} else if (!this.isMultiSelect) {
this.valueToEmit = this.value;
}
this.handleChange(this.valueToEmit);
},
handleClick(e) {
this.handleSelectionChange(e);
this.$emit('update:modelValue', this.valueToEmit);
},
handleFocus() {
handleButtonComponentFocus({
groupName: this.groupName
});
},
handleBlur() {
handleInputComponentBlur({
groupName: this.groupName,
onButtonQuestionLostFocusCallback: this.handlePushClickEventToGACheck
});
}
}
};
</script>
<style lang="scss" scoped>
input {
opacity: 0;
height: 0.1px; // NOTE: cannot be zero or Safari can't put focus on it
width: 0;
position: absolute;
}
</style>