boussole-pluss/frontend/components/Rating.vue

150 lines
2.7 KiB
Vue
Raw Permalink Normal View History

<script lang="ts" setup>
2022-10-07 14:15:53 +00:00
import {randomUUID} from "uncrypto";
2022-10-07 14:15:53 +00:00
const MAX_VALUE = 10;
2022-10-07 14:15:53 +00:00
const props = defineProps({
initialValue: Number,
color: {
2022-10-07 14:15:53 +00:00
type: String,
required: false,
2022-10-07 14:15:53 +00:00
default: "#DC6A00"
},
});
2022-10-07 14:15:53 +00:00
const emit = defineEmits(["rate"]);
2022-10-07 14:15:53 +00:00
const checkedValue = ref(0);
const componentId = ref();
2022-10-07 14:15:53 +00:00
onMounted(() => {
componentId.value = randomUUID();
checkedValue.value = props.initialValue;
});
2022-10-07 14:15:53 +00:00
const index = computed(() =>
Array.apply(0, Array(MAX_VALUE)).map((_, b) => {
2022-10-07 14:15:53 +00:00
return b + 1;
}).reverse()
);
2022-10-07 14:15:53 +00:00
const cssVars = computed(() => {
return {
'--color': props.color
2022-10-07 14:15:53 +00:00
}
});
2022-10-07 14:15:53 +00:00
function setChecked(index: number) {
if (index < 1) {
index = 1;
} else if (index > MAX_VALUE) {
index = MAX_VALUE;
2022-10-07 14:15:53 +00:00
}
checkedValue.value = index;
emit('rate', index);
}
2022-10-07 14:15:53 +00:00
function increment() {
setChecked(checkedValue.value + 1);
}
2022-10-07 14:15:53 +00:00
function decrement() {
setChecked(checkedValue.value - 1);
}
2022-10-07 14:15:53 +00:00
function isChecked(index: number) {
return checkedValue.value >= index;
}
2022-10-07 14:15:53 +00:00
function handleKeyUp(event: KeyboardEvent) {
switch (event.key) {
case "ArrowLeft":
decrement();
break;
case "ArrowRight":
increment();
break;
2022-10-07 14:15:53 +00:00
}
event.preventDefault();
return false;
2022-10-07 14:15:53 +00:00
}
</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>
2022-10-07 14:15:53 +00:00
<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>