Newer
Older
<script setup lang="ts">
import type { IRatingProps } from '@interfaces/componentsProps';

Дмитрий Малюгин
committed
import { computed, type Ref, ref, watch } from 'vue';
import { iconsSet } from '@/common/constants/icons';

Дмитрий Малюгин
committed
import StarFilledIcon from '@icons/Mono/StarFilledIcon.vue';
import { convertThemeToColor } from '@helpers/common';
const props = withDefaults(defineProps<IRatingProps>(), {
count: 5,
gap: '5px',
size: 'normal',
theme: 'black',
darknessTheme: '500',
const value = defineModel({
default: 0,
}) as Ref<number>;

Дмитрий Малюгин
committed
const emit = defineEmits(['update']);
if (props.value) {
value.value = props.value;
}

Дмитрий Малюгин
committed
const propValue = computed(() => props.value);
watch(propValue, () => (value.value = propValue.value ?? value.value));

Дмитрий Малюгин
committed
watch(value, () => emit('update', value));
const themeColor = computed(() => convertThemeToColor(props.theme, props.darknessTheme));
const offColor = computed(() => (props.offTheme ? convertThemeToColor(props.offTheme, props.darknessTheme) : null));
const themeColorOnHover = computed(() => convertThemeToColor(props.offTheme ?? props.theme, '200'));
const iconSize = computed(() => {
const size = props.size;
if (size === 'normal') return '20px';
if (size === 'large') return '30px';
if (size === 'huge') return '40px';
return '10px';
});
const onActiveClick = (index: number) => {
if (value.value > index) {
value.value = index;
return;
}
value.value = 0;
};
<ul class="list" :style="`gap: ${gap}`">
<li v-for="index of Array(count).keys()" :key="index" class="item iconSize">
<div v-show="value < index + 1 && !$slots.offIcon" class="iconSize iconContainer">
class="icon absoluteIcon"
:is="iconsSet['Star']"
:color="offColor ?? themeColor"
@pointerenter="onHoverIndex = index"
@pointerleave="onHoverIndex = null"
:size="iconSize"
/>
<Transition>
<component
class="absoluteIcon"
v-show="onHoverIndex === index"
:is="iconsSet['Star']"
:color="themeColorOnHover"
@pointerenter="onHoverIndex = index"
@pointerleave="onHoverIndex = null"
@click="value = index + 1"
:size="iconSize"
/>
</Transition>
</div>
<div v-show="value < index + 1" @click="value = index + 1">
<slot name="offIcon" :size="iconSize"></slot>
</div>
<StarFilledIcon
v-show="value >= index + 1 && !$slots.onIcon"
:color="themeColor"
:size="iconSize"
class="absoluteIcon"
@click="onActiveClick(index + 1)"
/>
<div class="iconSize" v-show="value >= index + 1" @click="onActiveClick(index + 1)">
<slot name="onIcon" :size="iconSize"></slot>
</div>
</li>
</ul>
</template>
<style scoped>
.list {
display: flex;
}
.item {
position: relative;
cursor: pointer;
}
.icon {
opacity: 1;
transition: opacity 0.15s ease-in-out;
:hover {
opacity: 0;
}
}
position: absolute;
top: 0;
left: 0;
}
.iconSize {
width: v-bind(iconSize);
height: v-bind(iconSize);
}