150 lines
2.7 KiB
Vue
150 lines
2.7 KiB
Vue
<script lang="ts" setup>
|
|
|
|
import {randomUUID} from "uncrypto";
|
|
|
|
const MAX_VALUE = 10;
|
|
|
|
const props = defineProps({
|
|
initialValue: Number,
|
|
color: {
|
|
type: String,
|
|
required: false,
|
|
default: "#DC6A00"
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(["rate"]);
|
|
|
|
const checkedValue = ref(0);
|
|
const componentId = ref();
|
|
|
|
onMounted(() => {
|
|
componentId.value = randomUUID();
|
|
checkedValue.value = props.initialValue;
|
|
});
|
|
|
|
const index = computed(() =>
|
|
Array.apply(0, Array(MAX_VALUE)).map((_, b) => {
|
|
return b + 1;
|
|
}).reverse()
|
|
);
|
|
|
|
const cssVars = computed(() => {
|
|
return {
|
|
'--color': props.color
|
|
}
|
|
});
|
|
|
|
function setChecked(index: number) {
|
|
if (index < 1) {
|
|
index = 1;
|
|
} else if (index > MAX_VALUE) {
|
|
index = MAX_VALUE;
|
|
}
|
|
checkedValue.value = index;
|
|
emit('rate', index);
|
|
}
|
|
|
|
function increment() {
|
|
setChecked(checkedValue.value + 1);
|
|
}
|
|
|
|
function decrement() {
|
|
setChecked(checkedValue.value - 1);
|
|
}
|
|
|
|
function isChecked(index: number) {
|
|
return checkedValue.value >= index;
|
|
}
|
|
|
|
function handleKeyUp(event: KeyboardEvent) {
|
|
switch (event.key) {
|
|
case "ArrowLeft":
|
|
decrement();
|
|
break;
|
|
case "ArrowRight":
|
|
increment();
|
|
break;
|
|
}
|
|
event.preventDefault();
|
|
return false;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div :style="cssVars">
|
|
<div class="rating" tabindex="0" @keyup="handleKeyUp">
|
|
<span v-for="i in index" :key="i" :class="{ checked: isChecked(i) }">
|
|
<input
|
|
:id="'rating' + componentId + '-' + i"
|
|
type="radio" name="rating" :value="i"
|
|
@click="setChecked(i)">
|
|
<label :for="'rating' + componentId + '-' + i" :aria-label="i"></label>
|
|
</span>
|
|
</div>
|
|
<div class="legend" aria-hidden="true">
|
|
<span>pas du tout</span>
|
|
<span>en partie</span>
|
|
<span>totalement</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
@import "assets/css/spacing";
|
|
@import "assets/css/font";
|
|
|
|
$width: 401px;
|
|
$input_width: 32px;
|
|
|
|
.rating {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
flex-direction: row-reverse;
|
|
max-width: $width;
|
|
}
|
|
|
|
.rating span {
|
|
position: relative;
|
|
}
|
|
|
|
.rating span input {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
opacity: 0;
|
|
}
|
|
|
|
.rating span label {
|
|
display: inline-block;
|
|
width: $input_width;
|
|
height: $input_width;
|
|
color: $white;
|
|
line-height: $input_width;
|
|
border-radius: 50%;
|
|
border: 1px solid $black;
|
|
}
|
|
|
|
.rating span:hover ~ span label,
|
|
.rating span:hover label,
|
|
.rating span.checked label,
|
|
.rating span.checked ~ span label {
|
|
background-color: var(--color);
|
|
color: #FFF;
|
|
border: 0;
|
|
cursor: pointer;
|
|
width: $input_width;
|
|
}
|
|
|
|
.legend {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
max-width: $width;
|
|
color: $gray_3;
|
|
font-weight: 400;
|
|
font-size: $small-font-size;
|
|
}
|
|
</style>
|