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

152 lines
No EOL
3.7 KiB
Vue

<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 -->
<input type="checkbox" :id="meal.id" :checked="isChecked(meal)" @change="mealCheckChanged" />
<label :for="meal.id" :style="getImageStyling(meal)">
{{ formatDate(meal.meal_date) }}
</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;
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;
color: #777;
}
input[type="checkbox"]:checked + label {
border: 3px solid #3d5447;
color: #3d5447;
text-shadow: #ccc 0 0 0.1em;
}
/* 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>
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;
}
export default {
name: 'MealSelectionList',
props: {
meals: Array,
checked: Array,
},
methods: {
formatDate,
getImageStyling(meal) {
const imageUrl = getUrl(meal);
if (!imageUrl) {
return null;
}
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);
}
},
isChecked(meal) {
for (const checkedMeal of this.checked) {
if (checkedMeal.id === meal.id) {
return true;
}
}
return false;
}
}
}
</script>