feature: Select component added

This commit is contained in:
Paul Valerie GOMA 2025-07-23 16:54:52 +02:00
parent 0197ba5df2
commit 961e15ed50

View File

@ -0,0 +1,177 @@
<script setup lang="ts">
import Select from 'primevue/select';
import type IVSelect from './IVSelect.type';
import { useId, computed, watch, ref } from 'vue';
import VLabel from '../label/VLabel.vue';
import VHint from '../hint/VHint.vue';
import styles from '@visua/typography.module.css';
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(defineProps<IVSelect>(), {
hint: '',
successMessage: '',
errorMessage: '',
selectId: () => `select-${useId()}`,
label: '',
required: false,
disabled: false,
options: undefined,
name: '',
defaultUnselectedText: 'Sélectionner une option',
filter: false,
editable: false,
optionLabel: undefined,
optionValue: undefined,
filterMessage: 'Les résultats sont disponibles',
selectionMessage: 'Elements sélectionnés',
emptySelectionMessage: 'Aucun élément sélectionné',
emptyFilterMessage: 'Aucun résultat trouvé',
emptyMessage: 'Aucune option disponible',
optionTemplate: false,
})
const emit = defineEmits([
'update:modelValue',
'value-change',
'change',
'focus',
'blur',
'before-show',
'before-hide',
'show',
'hide',
'filter',
])
const localModelValue = ref(props.modelValue);
watch(() => props.modelValue, (newVal) => {
if(localModelValue.value !== newVal){
localModelValue.value = newVal;
}
})
watch(localModelValue, (newVal) => {
if(props.modelValue !== newVal){
emit('update:modelValue', newVal);
}
});
const message = computed(() => {
return props.errorMessage || props.successMessage
})
const messageState = computed(() => {
return props.errorMessage ? 'error' : 'valid'
})
const typeMessage = computed(() => {
if (props.errorMessage) return 'alert';
else if (props.successMessage) return 'success';
else return undefined;
});
const labelState = computed(() => {
if(!props.errorMessage && !props.successMessage && !props.disabled) return 'default';
else if(!props.errorMessage && props.successMessage && !props.disabled) return 'success';
else if(props.errorMessage && !props.successMessage && !props.disabled) return 'error';
else return undefined
})
</script>
<template>
<div class="main-container">
<VLabel
:for="props.selectId"
:label="props.label"
:required="!props.disabled"
:disabled="props.disabled"
:type="labelState"
:hint="props.hint"
>
<template #required-type v-if="props.required">
<slot name="required-type"/>
</template>
</VLabel>
<Select
:name="props.name || props.selectId"
:options="props.options"
:optionLabel="props.optionLabel"
:optionValue="props.optionValue"
:disabled="props.disabled"
:aria-disabled="props.disabled"
:filter="props.filter"
:editable="props.editable"
:placeholder="props.placeholder"
:filter-message="props.filterMessage"
:selection-message="props.selectionMessage"
:empty-selection-message="props.emptySelectionMessage"
:empty-filter-message="props.emptyFilterMessage"
:empty-message="props.emptyMessage"
v-bind="$attrs"
v-model:model-value="localModelValue"
@update:model-value="emit('update:modelValue', $event)"
@change="emit('change', $event)"
@blur="emit('blur', $event)"
@focus="emit('focus', $event)"
@value-change="emit('value-change', $event)"
@before-hide="emit('before-hide', $event)"
@before-show="emit('before-show', $event)"
@show="emit('show', $event)"
@hide="emit('hide', $event)"
@filter="emit('filter', $event)"
class="p-select"
:class="[
styles['text-body-MD-standard-text-Regular'],
{
'disabled': props.disabled,
'error': props.errorMessage && !props.successMessage && !props.disabled,
'success': !props.errorMessage && props.successMessage && !props.disabled,
}
]"
>
<template v-if="props.optionTemplate" #option="{option, selected, index}">
<slot name="option" :option="option" :selected="selected" :index="index"/>
</template>
</Select>
<div
:id="`select-${messageState}-desc-${messageState}`"
role="alert"
aria-live="assertive"
>
<VHint :title="message" :type="typeMessage" icon/>
</div>
</div>
</template>
<style lang="css" scoped>
.main-container {
width: 100%;
display: flex;
flex-direction: column;
align-items: start;
gap: 0.5rem;
}
.p-select{
width: 100%;
height: 2.5rem;
}
.p-select.disabled{
--p-select-border-color: var(--border-disabled-grey);
--p-select-dropdown-color: var(--text-disabled-grey);
}
.p-select.error{
--p-select-border-color: var(--border-plain-error);
--p-select-hover-border-color: var(--border-plain-error);
--p-select-focus-border-color: var(--border-plain-error);
}
.p-select.success{
--p-select-border-color: var(--border-plain-success);
--p-select-hover-border-color: var(--border-plain-success);
--p-select-focus-border-color: var(--border-plain-success);
}
</style>