munch-ease-frontend/src/components/shopping/MealSelectionList.vue

155 lines
3.8 KiB
Vue
Raw Normal View History

2024-05-11 06:03:29 +00:00
<template>
<ul>
<li v-for="meal in meals" :key="meal.id">
<!-- Have a checkbox and card for each meal, show the image and name -->
<!-- Emit meal-selected event on checked and meal-unselected on unchecked -->
2024-05-20 12:16:39 +00:00
<input type="checkbox" :id="meal.id" :checked="isChecked(meal)" @change="mealCheckChanged" :disabled="!!meal.purchase_date" />
2024-05-11 06:03:29 +00:00
<label :for="meal.id" :style="getImageStyling(meal)">
2024-05-25 02:09:27 +00:00
{{ formatDate(meal.suggested_date) }}
2024-05-11 06:03:29 +00:00
</label>
</li>
</ul>
</template>
<style scoped>
ul {
padding: 0;
}
li {
display: inline-block;
list-style-type: none;
border: 1px solid #ccc;
border-radius: 0.5em;
margin-bottom: 1em;
padding: none;
overflow: hidden;
text-align: center;
}
/* Hide the default checkbox formatting, and format the card instead */
input[type="checkbox"] {
display: none;
}
label {
display: inline-block;
2024-05-12 03:32:18 +00:00
padding: 0.1vh 0.3em;
/* Help the visibility of the text over the image */
background-color: rgba(255, 255, 255, 0.9);
border: 3px solid transparent;
border-radius: 0.5em;
margin: 0;
font-weight: normal;
cursor: pointer;
2024-05-20 12:16:39 +00:00
color: #3d5447;
2024-05-11 06:03:29 +00:00
}
input[type="checkbox"]:checked + label {
2024-05-12 03:32:18 +00:00
border: 3px solid #3d5447;
text-shadow: #ccc 0 0 0.1em;
2024-05-11 06:03:29 +00:00
}
2024-05-20 12:16:39 +00:00
input[type="checkbox"]:disabled + label {
cursor: not-allowed;
}
2024-05-11 06:03:29 +00:00
/* Show the image as the background image of the card */
.meal-image {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Position the text in the center of the card */
label {
display: flex;
justify-content: center;
align-items: center;
font-size: larger;
font-weight: bold;
}
</style>
<script>
2024-05-12 03:32:18 +00:00
const nth = (d) => {
if (d > 3 && d < 21) return 'th';
switch (d % 10) {
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
};
const formatDate = (date) => {
return `${date.toLocaleDateString('en-AU', { weekday: 'short' })} ${date.getDate()}${nth(date.getDate())}`
}
const getUrl = (meal) => {
// First non empty value in meal.recipe/image_urls
for (const recipe of meal.recipes) {
if (recipe.image_urls.length && recipe.image_urls[0]) {
return recipe.image_urls[0];
}
}
// First meal.extra_ingredient with a product with an image
for (const ingredient of meal.extra_ingredients) {
if (ingredient.product) {
if (ingredient.product.img_large) {
return ingredient.product.img_large;
}
if (ingredient.product.img_small) {
return ingredient.product.img_small;
}
}
}
return null;
}
2024-05-11 06:03:29 +00:00
export default {
name: 'MealSelectionList',
2024-05-13 03:45:46 +00:00
props: {
meals: Array,
checked: Array,
2024-05-11 06:03:29 +00:00
},
methods: {
2024-05-12 03:32:18 +00:00
formatDate,
2024-05-11 06:03:29 +00:00
getImageStyling(meal) {
2024-05-12 03:32:18 +00:00
const imageUrl = getUrl(meal);
if (!imageUrl) {
return null;
2024-05-11 06:03:29 +00:00
}
2024-05-12 03:32:18 +00:00
const opacity = 0.7;
return {background: `linear-gradient(to bottom, rgba(255, 255, 255, ${ opacity }) 0%, rgba(255, 255, 255, ${ opacity }) 100%), url('${imageUrl}') center/cover no-repeat`};
},
mealCheckChanged(event) {
const mealId = parseInt(event.target.id);
const meal = this.meals.find(m => m.id === mealId);
if (event.target.checked) {
this.$emit('meal-selected', meal);
} else {
this.$emit('meal-unselected', meal);
2024-05-11 06:03:29 +00:00
}
2024-05-13 03:45:46 +00:00
},
2024-05-18 07:05:28 +00:00
isChecked(meal) {
for (const checkedMeal of this.checked) {
if (checkedMeal.id === meal.id) {
return true;
}
}
return false;
}
2024-05-11 06:03:29 +00:00
}
}
</script>