moved many compoents to composition API

This commit is contained in:
vabene1111
2024-04-21 15:58:31 +02:00
parent ce6c43fb62
commit e040a10096
13 changed files with 342 additions and 347 deletions

View File

@@ -1,44 +1,39 @@
<template>
<v-table density="compact" v-if="ingredients.length > 0">
<v-table density="compact" v-if="props.ingredients.length > 0">
<tbody>
<IngredientsTableRow v-for="i in ingredients" :ingredient="i" :key="i.id" :show-notes="showNotes" :draggable="draggable"></IngredientsTableRow>
<IngredientsTableRow v-for="i in props.ingredients" :ingredient="i" :key="i.id" :show-notes="props.showNotes" :draggable="props.draggable"></IngredientsTableRow>
</tbody>
</v-table>
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue'
import {Ingredient, Step} from "@/openapi";
<script lang="ts" setup>
import {onMounted, PropType, ref} from 'vue'
import {Ingredient} from "@/openapi";
import IngredientsTableRow from "@/components/display/IngredientsTableRow.vue";
import draggable from 'vuedraggable'
export default defineComponent({
name: "IngredientsTable",
components: {IngredientsTableRow, draggable},
props: {
ingredients: {
type: Array as PropType<Array<Ingredient>>,
default: [],
},
showNotes: {
type: Boolean,
default: true
},
draggable: {
type: Boolean,
},
const props = defineProps({
ingredients: {
type: Array as PropType<Array<Ingredient>>,
default: [],
},
data() {
return {
mutable_ingredients: [] as Ingredient[]
}
showNotes: {
type: Boolean,
default: true
},
draggable: {
type: Boolean,
},
mounted() {
this.mutable_ingredients = this.ingredients
}
})
const mutable_ingredients = ref([] as Ingredient[])
onMounted(() => {
mutable_ingredients.value = props.ingredients
})
</script>

View File

@@ -1,18 +1,18 @@
<template>
<tr>
<template v-if="ingredient.isHeader">
<td colspan="4"><b>{{ ingredient.note }}</b></td>
<template v-if="props.ingredient.isHeader">
<td colspan="4"><b>{{ props.ingredient.note }}</b></td>
</template>
<template v-else>
<td>{{ ingredient.amount }}</td>
<td><span v-if="ingredient.unit != null">{{ ingredient.unit.name }}</span></td>
<td><span v-if="ingredient.food != null">{{ ingredient.food.name }}</span></td>
<td v-if="showNotes">
<v-icon class="far fa-comment float-right" v-if="ingredient.note != '' && ingredient.note != undefined" @click="show_tooltip = !show_tooltip">
<v-tooltip v-model="show_tooltip" activator="parent" location="start">{{ ingredient.note }}</v-tooltip>
<td>{{ props.ingredient.amount }}</td>
<td><span v-if="props.ingredient.unit != null">{{ props.ingredient.unit.name }}</span></td>
<td><span v-if="props.ingredient.food != null">{{ props.ingredient.food.name }}</span></td>
<td v-if="props.showNotes">
<v-icon class="far fa-comment float-right" v-if="props.ingredient.note != '' && props.ingredient.note != undefined" @click="showTooltip = !showTooltip">
<v-tooltip v-model="showTooltip" activator="parent" location="start">{{ props.ingredient.note }}</v-tooltip>
</v-icon>
</td>
<td v-if="draggable">
<td v-if="props.draggable">
<i class="fas fa-grip-lines drag-handle cursor-move"></i>
</td>
</template>
@@ -20,32 +20,26 @@
</tr>
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue'
<script setup lang="ts">
import {PropType, ref} from 'vue'
import {Ingredient} from "@/openapi";
export default defineComponent({
name: "IngredientsTableRow",
components: {},
props: {
ingredient: {
type: {} as PropType<Ingredient>,
required: true
},
showNotes: {
type: Boolean,
default: true
},
draggable: {
type: Boolean,
},
const props = defineProps({
ingredient: {
type: {} as PropType<Ingredient>,
required: true
},
data() {
return {
show_tooltip: false,
}
showNotes: {
type: Boolean,
default: true
},
draggable: {
type: Boolean,
},
})
const showTooltip = ref(false)
</script>
<style scoped>

View File

@@ -1,26 +1,20 @@
<template>
<div v-if="keywords">
<v-chip class="ms-1" :color="color" :size="size" :variant="variant" v-for="k in keywords"> {{ k.label }}</v-chip>
<div v-if="props.keywords">
<v-chip class="ms-1" :color="props.color" :size="props.size" :variant="props.variant" v-for="k in props.keywords"> {{ k.label }}</v-chip>
</div>
</template>
<script lang="ts">
<script setup lang="ts">
import {Keyword, KeywordLabel} from "@/openapi";
import {PropType} from "vue";
export default {
name: 'KeywordsBar',
mixins: [],
props: {
keywords: Array as PropType<Array<Keyword> | Array<KeywordLabel> | undefined>,
size: {type: String, default: 'x-small'},
color: {type: String, default: ''},
variant: {type: String as PropType<NonNullable<"tonal" | "flat" | "text" | "elevated" | "outlined" | "plain"> | undefined>, default: 'tonal'},
},
computed: {
},
methods: {}
}
const props = defineProps({
keywords: Array as PropType<Array<Keyword> | Array<KeywordLabel> | undefined>,
size: {type: String, default: 'x-small'},
color: {type: String, default: ''},
variant: {type: String as PropType<NonNullable<"tonal" | "flat" | "text" | "elevated" | "outlined" | "plain"> | undefined>, default: 'tonal'},
})
</script>

View File

@@ -1,64 +1,68 @@
<template>
<template v-if="!loading">
<v-card :to="`/recipe/${recipe.id}`" :style="{'height': height}">
<template v-if="!componentProps.loading">
<v-card :to="`/recipe/${componentProps.recipe.id}`" :style="{'height': componentProps.height}">
<v-tooltip
class="align-center justify-center"
location="top center" origin="overlap"
no-click-animation
:open-on-hover="recipe.description != null && recipe.description != ''"
:open-on-hover="componentProps.recipe.description != null && componentProps.recipe.description != ''"
contained
>
<template v-slot:activator="{ props }">
<v-img cover
height="60%"
:src="recipeImageUrl"
<recipe-image
height="60%"
width="100%"
:recipe="componentProps.recipe"
>
<v-chip size="x-small" prepend-icon="fa fa-clock" label color="light" variant="elevated"
class="float-start ms-1 mt-1" v-if="recipe.workingTime != undefined && recipe.workingTime > 0">
{{ recipe.workingTime }}
</v-chip>
<v-chip size="x-small" prepend-icon="fa fa-pause" label color="secondary" variant="elevated"
class="float-start ms-1 mt-1" v-if="recipe.waitingTime != undefined && recipe.waitingTime > 0">
{{ recipe.waitingTime }}
</v-chip>
<template #overlay>
<v-chip size="x-small" prepend-icon="fa fa-clock" label color="light" variant="elevated"
class="float-start ms-1 mt-1" v-if="componentProps.recipe.workingTime != undefined && componentProps.recipe.workingTime > 0">
{{ recipe.workingTime }}
</v-chip>
<v-chip size="x-small" prepend-icon="fa fa-pause" label color="secondary" variant="elevated"
class="float-start ms-1 mt-1" v-if="componentProps.recipe.waitingTime != undefined && componentProps.recipe.waitingTime > 0">
{{ recipe.waitingTime }}
</v-chip>
<keywords-component variant="flat" :keywords="recipe.keywords"></keywords-component>
</v-img>
<v-divider class="p-0" v-if="recipe.image == null"></v-divider>
<keywords-component variant="flat" :keywords="componentProps.recipe.keywords"></keywords-component>
</template>
</recipe-image>
<v-divider class="p-0" v-if="componentProps.recipe.image == null"></v-divider>
</template>
<div v-if="recipe.description != null && recipe.description != ''">
{{ recipe.description }}
<div v-if="componentProps.recipe.description != null && componentProps.recipe.description != ''">
{{ componentProps.recipe.description }}
</div>
</v-tooltip>
<v-card-item>
<v-card-title>
{{ recipe.name }}
<recipe-context-menu class="float-end" :recipe="recipe"></recipe-context-menu>
{{ componentProps.recipe.name }}
<recipe-context-menu class="float-end" :recipe="recipe"></recipe-context-menu>
</v-card-title>
<v-card-subtitle>by {{ recipe.createdBy}}</v-card-subtitle>
<v-card-subtitle>by {{ componentProps.recipe.createdBy }}</v-card-subtitle>
<!-- <v-card-subtitle v-if="show_keywords">-->
<!-- <keywords-component :keywords="recipe.keywords"></keywords-component>-->
<!-- </v-card-subtitle>-->
<!-- <v-rating-->
<!-- v-if="recipe.rating != null"-->
<!-- v-model="recipe.rating"-->
<!-- color="amber"-->
<!-- density="comfortable"-->
<!-- half-increments-->
<!-- readonly-->
<!-- size="x-small"-->
<!-- ></v-rating>-->
<!-- <v-card-subtitle v-if="show_keywords">-->
<!-- <keywords-component :keywords="recipe.keywords"></keywords-component>-->
<!-- </v-card-subtitle>-->
<!-- <v-rating-->
<!-- v-if="recipe.rating != null"-->
<!-- v-model="recipe.rating"-->
<!-- color="amber"-->
<!-- density="comfortable"-->
<!-- half-increments-->
<!-- readonly-->
<!-- size="x-small"-->
<!-- ></v-rating>-->
</v-card-item>
</v-card>
</template>
<template v-else>
<v-card :style="{'height': height}">
<v-card :style="{'height': componentProps.height}">
<v-img src="../../assets/recipe_no_image.svg" cover height="60%"></v-img>
<v-card-title>
<v-skeleton-loader type="heading"></v-skeleton-loader>
@@ -72,29 +76,22 @@
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue'
<script setup lang="ts">
import {PropType} from 'vue'
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
import {Recipe, RecipeOverview} from "@/openapi";
import recipeNoImage from '@/assets/recipe_no_image.svg';
import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
export default defineComponent({
name: "RecipeCard",
components: {RecipeContextMenu, KeywordsComponent},
props: {
recipe: {type: {} as PropType<Recipe | RecipeOverview>, required: true,},
loading: {type: Boolean, required: false},
show_keywords: {type: Boolean, required: false},
show_description: {type: Boolean, required: false},
height: {type: String, required: false, default: '25vh'},
},
computed: {
recipeImageUrl: function () {
return (this.recipe.image != null) ? this.recipe.image : recipeNoImage
}
}
import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
import RecipeImage from "@/components/display/RecipeImage.vue";
const componentProps = defineProps({
recipe: {type: {} as PropType<Recipe | RecipeOverview>, required: true,},
loading: {type: Boolean, required: false},
show_keywords: {type: Boolean, required: false},
show_description: {type: Boolean, required: false},
height: {type: String, required: false, default: '25vh'},
})
</script>
<style scoped>

View File

@@ -1,28 +1,37 @@
<template>
<v-img :cover="cover" :style="{'height': height, 'width': width,}" :src="image" alt="Recipe Image"/>
<v-img :cover="cover" :style="{'height': height, 'width': width,}" :src="image" alt="Recipe Image">
<slot name="overlay">
</slot>
</v-img>
</template>
<script setup lang="ts">
import {computed, PropType} from "vue";
import {computed, PropType, watch} from "vue";
import {Recipe, RecipeOverview} from "@/openapi";
import recipeDefaultImage from '../../assets/recipe_no_image.svg'
const props = defineProps({
recipe: {type: {} as PropType<Recipe|RecipeOverview|undefined>, required: false, default: undefined},
recipe: {type: {} as PropType<Recipe | RecipeOverview | undefined>, required: false, default: undefined},
height: {type: String},
width: {type: String},
cover: {type: Boolean, default: true}
})
const image = computed(() => {
if(props.recipe != undefined && props.recipe.image != undefined){
if (props.recipe != undefined && props.recipe.image != undefined) {
return props.recipe.image
} else {
return recipeDefaultImage
}
})
watch(() => props.recipe, () => {
console.log('changed')
})
</script>
<style scoped>

View File

@@ -1,16 +1,22 @@
<template>
<template v-if="recipe.name != undefined">
<template v-if="props.recipe.name != undefined">
<v-card class="mt-md-4 rounded-0">
<v-img max-height="25vh" cover lazy :src="recipe.image" v-if="recipe.image != undefined" class="align-end">
<v-chip class="ms-2" color="primary" variant="flat" size="x-small">by {{ recipe.createdBy}}</v-chip>
<KeywordsComponent variant="flat" class="ms-1 mb-2" :keywords="recipe.keywords"></KeywordsComponent>
</v-img>
<recipe-image
max-height="25vh"
:recipe="props.recipe"
>
<template #overlay>
<v-chip class="ms-2" color="primary" variant="flat" size="x-small">by {{ props.recipe.createdBy }}</v-chip>
<KeywordsComponent variant="flat" class="ms-1 mb-2" :keywords="props.recipe.keywords"></KeywordsComponent>
</template>
</recipe-image>
<v-card>
<v-sheet class="d-flex align-center">
<span class="ps-2 text-h5 flex-grow-1" :class="{'text-truncate': !showFullRecipeName}" @click="showFullRecipeName = !showFullRecipeName">{{ recipe.name }}</span>
<span class="ps-2 text-h5 flex-grow-1" :class="{'text-truncate': !showFullRecipeName}" @click="showFullRecipeName = !showFullRecipeName">{{ props.recipe.name }}</span>
<recipe-context-menu :recipe="recipe"></recipe-context-menu>
</v-sheet>
</v-card>
@@ -20,11 +26,11 @@
<v-container>
<v-row class="text-center text-body-2">
<v-col class="pt-1 pb-1">
<i class="fas fa-cogs fa-fw mr-1"></i> {{ recipe.workingTime }} min<br/>
<i class="fas fa-cogs fa-fw mr-1"></i> {{ props.recipe.workingTime }} min<br/>
<div class="text-grey">Working Time</div>
</v-col>
<v-col class="pt-1 pb-1">
<div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ recipe.waitingTime }} min</div>
<div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ props.recipe.waitingTime }} min</div>
<div class="text-grey">Waiting Time</div>
</v-col>
<v-col class="pt-1 pb-1">
@@ -32,7 +38,7 @@
<template #activator>
<div class="cursor-pointer">
<i class="fas fa-sort-numeric-up fa-fw mr-1"></i> {{ servings }} <br/>
<div class="text-grey"><span v-if="recipe?.servingsText">{{ recipe.servingsText }}</span><span v-else>Servings</span></div>
<div class="text-grey"><span v-if="props.recipe?.servingsText">{{ props.recipe.servingsText }}</span><span v-else>Servings</span></div>
</div>
</template>
</NumberScalerDialog>
@@ -41,21 +47,21 @@
</v-container>
</v-card>
<v-card class="mt-1" v-if="recipe.steps.length > 1">
<StepsOverview :steps="recipe.steps"></StepsOverview>
<v-card class="mt-1" v-if="props.recipe.steps.length > 1">
<StepsOverview :steps="props.recipe.steps"></StepsOverview>
</v-card>
<v-card class="mt-1" v-for="(step, index) in recipe.steps" :key="step.id">
<Step :step="step" :step-number="index+1" :ingredient_factor="ingredient_factor"></Step>
<v-card class="mt-1" v-for="(step, index) in props.recipe.steps" :key="step.id">
<Step :step="step" :step-number="index+1" :ingredient_factor="ingredientFactor"></Step>
</v-card>
<recipe-activity :recipe="recipe"></recipe-activity>
</template>
</template>
<script lang="ts">
<script setup lang="ts">
import {defineComponent, PropType} from 'vue'
import {computed, defineComponent, PropType, ref, watch} from 'vue'
import {ApiApi, Ingredient, Recipe} from "@/openapi"
import KeywordsBar from "@/components/display/KeywordsBar.vue"
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue"
@@ -65,39 +71,28 @@ import Step from "@/components/display/Step.vue";
import RecipeActivity from "@/components/display/RecipeActivity.vue";
import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
import KeywordsComponent from "@/components/display/KeywordsBar.vue";
import RecipeImage from "@/components/display/RecipeImage.vue";
export default defineComponent({
name: "RecipeView",
components: {KeywordsComponent, RecipeContextMenu, RecipeActivity, Step, StepsOverview, IngredientsTable, NumberScalerDialog, KeywordsBar},
computed: {
ingredient_factor: function () {
return this.servings / this.recipe.servings
},
},
data() {
return {
servings: 1,
showFullRecipeName: false,
}
},
watch: {
'recipe.servings': function () {
if (this.recipe.servings) {
this.servings = this.recipe.servings
}
}
},
props: {
recipe: {
type: Object as PropType<Recipe>,
required: true
}
},
mounted() {
},
methods: {}
const props = defineProps({
recipe: {
type: Object as PropType<Recipe>,
required: true
}
})
const servings = ref(1)
const showFullRecipeName = ref(false)
const ingredientFactor = computed(() => {
return servings.value / ((props.recipe.servings != undefined) ? props.recipe.servings : 1)
})
watch(() => props.recipe.servings, () => {
if (props.recipe.servings) {
servings.value = props.recipe.servings
}
})
</script>
<style scoped>

View File

@@ -2,71 +2,59 @@
<v-card>
<v-card-title>
<v-row>
<v-col><span v-if="step.name">{{ step.name }}</span><span v-else>Step {{ stepNumber}}</span></v-col>
<v-col><span v-if="props.step.name">{{ props.step.name }}</span><span v-else>Step {{ props.stepNumber }}</span></v-col>
<v-col class="text-right">
<v-btn-group density="compact" variant="tonal">
<v-btn size="small" color="info" v-if="step.time != undefined && step.time > 0" @click="timerRunning = true"><i class="fas fa-stopwatch mr-1 fa-fw"></i> {{ step.time }}</v-btn>
<v-btn size="small" color="info" v-if="props.step.time != undefined && props.step.time > 0" @click="timerRunning = true"><i class="fas fa-stopwatch mr-1 fa-fw"></i> {{ props.step.time }}</v-btn>
<v-btn size="small" color="success" v-if="hasDetails" @click="stepChecked = !stepChecked"><i class="fas fa-fw" :class="{'fa-check': !stepChecked, 'fa-times': stepChecked}"></i></v-btn>
</v-btn-group>
</v-col>
</v-row>
</v-card-title>
<template v-if="!stepChecked">
<timer :seconds="step.time != undefined ? step.time*60 : 0" @stop="timerRunning = false" v-if="timerRunning"></timer>
<timer :seconds="props.step.time != undefined ? props.step.time*60 : 0" @stop="timerRunning = false" v-if="timerRunning"></timer>
<IngredientsTable :ingredients="step.ingredients"></IngredientsTable>
<IngredientsTable :ingredients="props.step.ingredients"></IngredientsTable>
<v-card-text v-if="step.instructionsMarkdown.length > 0">
<instructions :instructions_html="step.instructionsMarkdown" :ingredient_factor="ingredient_factor"></instructions>
<v-card-text v-if="props.step.instructionsMarkdown.length > 0">
<instructions :instructions_html="props.step.instructionsMarkdown" :ingredient_factor="ingredient_factor"></instructions>
</v-card-text>
</template>
</v-card>
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue'
<script setup lang="ts">
import {computed, defineComponent, PropType, ref} from 'vue'
import IngredientsTable from "@/components/display/IngredientsTable.vue";
import {Step} from "@/openapi";
import {DateTime, Duration, Interval} from "luxon";
import Instructions from "@/components/display/Instructions.vue";
import Timer from "@/components/display/Timer.vue";
export default defineComponent({
name: "Step",
components: {Timer, Instructions, IngredientsTable},
props: {
step: {
type: {} as PropType<Step>,
required: true,
},
stepNumber: {
type: Number,
required: false,
default: 1
},
ingredient_factor: {
type: Number,
required: true,
},
const props = defineProps({
step: {
type: {} as PropType<Step>,
required: true,
},
computed: {
hasDetails: function () {
return this.step.ingredients.length > 0 || (this.step.instruction != undefined && this.step.instruction.length > 0) || this.step.stepRecipeData != undefined || this.step.file != undefined
}
stepNumber: {
type: Number,
required: false,
default: 1
},
data() {
return {
timerRunning: false,
stepChecked: false,
}
ingredient_factor: {
type: Number,
required: true,
},
mounted() {
},
methods: {}
})
const timerRunning = ref(false)
const stepChecked = ref(false)
const hasDetails = computed(() => {
return props.step.ingredients.length > 0 || (props.step.instruction != undefined && props.step.instruction.length > 0) || props.step.stepRecipeData != undefined || props.step.file != undefined
})
</script>
<style scoped>

View File

@@ -4,7 +4,7 @@
<v-expansion-panel-title><i class="far fa-list-alt fa-fw me-2"></i> Steps Overview</v-expansion-panel-title>
<v-expansion-panel-text>
<v-container>
<v-row v-for="(s, i) in steps">
<v-row v-for="(s, i) in props.steps">
<v-col class="pa-1">
<b v-if="s.showAsHeader">{{ i + 1 }}. {{ s.name }} </b>
<IngredientsTable :ingredients="s.ingredients"></IngredientsTable>
@@ -18,22 +18,18 @@
</template>
<script lang="ts">
import {defineComponent, PropType} from 'vue'
<script setup lang="ts">
import {PropType} from 'vue'
import {Step} from "@/openapi";
import IngredientsTableRow from "@/components/display/IngredientsTableRow.vue";
import IngredientsTable from "@/components/display/IngredientsTable.vue";
export default defineComponent({
name: "StepsOverview",
components: {IngredientsTable, IngredientsTableRow},
props: {
steps: {
type: Array as PropType<Array<Step>>,
default: [],
},
}
const props = defineProps({
steps: {
type: Array as PropType<Array<Step>>,
default: [],
},
})
</script>