mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			102 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div class="rating-display">
 | |
|     <span
 | |
|       v-for="(star, index) in ratingDisplay"
 | |
|       :key="index"
 | |
|       class="star"
 | |
|       :class="{
 | |
|         'star-half': star === 'half',
 | |
|         'text-secondary': !useGroupStyle,
 | |
|         'text-grey-darken-1': useGroupStyle,
 | |
|       }"
 | |
|     >
 | |
|       <!-- We render both the full and empty stars for "half" stars because they're layered over each other -->
 | |
|       <span
 | |
|         v-if="star === 'empty' || star === 'half'"
 | |
|         class="star-empty"
 | |
|       >
 | |
|         ☆
 | |
|       </span>
 | |
|       <span
 | |
|         v-if="star === 'full' || star === 'half'"
 | |
|         class="star-full"
 | |
|       >
 | |
|         ★
 | |
|       </span>
 | |
|     </span>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script setup lang="ts">
 | |
| import { useLoggedInState } from "~/composables/use-logged-in-state";
 | |
| import { useUserSelfRatings } from "~/composables/use-users";
 | |
| 
 | |
| type Star = "full" | "half" | "empty";
 | |
| 
 | |
| const props = defineProps({
 | |
|   modelValue: {
 | |
|     type: Number,
 | |
|     default: 0,
 | |
|   },
 | |
|   recipeId: {
 | |
|     type: String,
 | |
|     default: "",
 | |
|   },
 | |
| });
 | |
| 
 | |
| const { isOwnGroup } = useLoggedInState();
 | |
| const { userRatings } = useUserSelfRatings();
 | |
| 
 | |
| const userRating = computed(() => {
 | |
|   return userRatings.value.find(r => r.recipeId === props.recipeId)?.rating ?? undefined;
 | |
| });
 | |
| 
 | |
| const ratingValue = computed(() => userRating.value || props.modelValue || 0);
 | |
| const useGroupStyle = computed(() => isOwnGroup.value && !userRating.value && props.modelValue);
 | |
| const ratingDisplay = computed<Star[]>(
 | |
|   () => {
 | |
|     const stars: Star[] = [];
 | |
| 
 | |
|     for (let i = 0; i < 5; i++) {
 | |
|       const diff = ratingValue.value - i;
 | |
|       if (diff >= 1) {
 | |
|         stars.push("full");
 | |
|       }
 | |
|       else if (diff >= 0.25) { // round to half star if rating is at least 0.25 but not quite a full star
 | |
|         stars.push("half");
 | |
|       }
 | |
|       else {
 | |
|         stars.push("empty");
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return stars;
 | |
|   },
 | |
| );
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" scoped>
 | |
| .rating-display {
 | |
|   display: inline-flex;
 | |
|   align-items: center;
 | |
|   gap: 1px;
 | |
| 
 | |
|   .star {
 | |
|     font-size: 18px;
 | |
|     transition: color 0.2s ease;
 | |
|     user-select: none;
 | |
|     position: relative;
 | |
|     display: inline-block;
 | |
|     &.star-half {
 | |
|       .star-full {
 | |
|         position: absolute;
 | |
|         left: 0;
 | |
|         top: 0;
 | |
|         width: 50%;
 | |
|         overflow: hidden;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </style>
 |