print view & container

This commit is contained in:
Kuchenpirat 2025-07-30 12:22:28 +00:00
commit a0881c451f
2 changed files with 143 additions and 176 deletions

View file

@ -8,24 +8,17 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import RecipePrintView from "~/components/Domain/Recipe/RecipePrintView.vue"; import RecipePrintView from "~/components/Domain/Recipe/RecipePrintView.vue";
import type { Recipe } from "~/lib/api/types/recipe"; import type { Recipe } from "~/lib/api/types/recipe";
export default defineNuxtComponent({ interface Props {
components: { recipe: Recipe;
RecipePrintView, scale?: number;
}, }
props: {
recipe: { withDefaults(defineProps<Props>(), {
type: Object as () => Recipe, scale: 1,
required: true,
},
scale: {
type: Number,
default: 1,
},
},
}); });
</script> </script>

View file

@ -166,7 +166,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue"; import RecipeTimeCard from "~/components/Domain/Recipe/RecipeTimeCard.vue";
import { useStaticRoutes } from "~/composables/api"; import { useStaticRoutes } from "~/composables/api";
@ -188,39 +188,29 @@ type InstructionSection = {
instructions: RecipeStep[]; instructions: RecipeStep[];
}; };
export default defineNuxtComponent({ interface Props {
components: { recipe: NoUndefinedField<Recipe>;
RecipeTimeCard, scale?: number;
}, dense?: boolean;
props: { }
recipe: { const props = withDefaults(defineProps<Props>(), {
type: Object as () => NoUndefinedField<Recipe>, scale: 1,
required: true, dense: false,
}, });
scale: {
type: Number,
default: 1,
},
dense: {
type: Boolean,
default: false,
},
},
setup(props) {
const i18n = useI18n();
const preferences = useUserPrintPreferences();
const { recipeImage } = useStaticRoutes();
const { imageKey } = usePageState(props.recipe.slug);
const { labels } = useNutritionLabels();
function sanitizeHTML(rawHtml: string) { const i18n = useI18n();
const preferences = useUserPrintPreferences();
const { recipeImage } = useStaticRoutes();
const { imageKey } = usePageState(props.recipe.slug);
const { labels } = useNutritionLabels();
function sanitizeHTML(rawHtml: string) {
return DOMPurify.sanitize(rawHtml, { return DOMPurify.sanitize(rawHtml, {
USE_PROFILES: { html: true }, USE_PROFILES: { html: true },
ALLOWED_TAGS: ["strong", "sup"], ALLOWED_TAGS: ["strong", "sup"],
}); });
} }
const servingsDisplay = computed(() => {
const servingsDisplay = computed(() => {
const { scaledAmountDisplay } = useScaledAmount(props.recipe.recipeYieldQuantity, props.scale); const { scaledAmountDisplay } = useScaledAmount(props.recipe.recipeYieldQuantity, props.scale);
return scaledAmountDisplay || props.recipe.recipeYield return scaledAmountDisplay || props.recipe.recipeYield
? i18n.t("recipe.yields-amount-with-text", { ? i18n.t("recipe.yields-amount-with-text", {
@ -228,28 +218,28 @@ export default defineNuxtComponent({
text: props.recipe.recipeYield, text: props.recipe.recipeYield,
}) as string }) as string
: ""; : "";
}); });
const yieldDisplay = computed(() => { const yieldDisplay = computed(() => {
const { scaledAmountDisplay } = useScaledAmount(props.recipe.recipeServings, props.scale); const { scaledAmountDisplay } = useScaledAmount(props.recipe.recipeServings, props.scale);
return scaledAmountDisplay ? i18n.t("recipe.serves-amount", { amount: scaledAmountDisplay }) as string : ""; return scaledAmountDisplay ? i18n.t("recipe.serves-amount", { amount: scaledAmountDisplay }) as string : "";
}); });
const recipeYield = computed(() => { const recipeYield = computed(() => {
if (servingsDisplay.value && yieldDisplay.value) { if (servingsDisplay.value && yieldDisplay.value) {
return sanitizeHTML(`${yieldDisplay.value}; ${servingsDisplay.value}`); return sanitizeHTML(`${yieldDisplay.value}; ${servingsDisplay.value}`);
} }
else { else {
return sanitizeHTML(yieldDisplay.value || servingsDisplay.value); return sanitizeHTML(yieldDisplay.value || servingsDisplay.value);
} }
}); });
const recipeImageUrl = computed(() => { const recipeImageUrl = computed(() => {
return recipeImage(props.recipe.id, props.recipe.image, imageKey.value); return recipeImage(props.recipe.id, props.recipe.image, imageKey.value);
}); });
// Group ingredients by section so we can style them independently // Group ingredients by section so we can style them independently
const ingredientSections = computed<IngredientSection[]>(() => { const ingredientSections = computed<IngredientSection[]>(() => {
if (!props.recipe.recipeIngredient) { if (!props.recipe.recipeIngredient) {
return []; return [];
} }
@ -279,10 +269,10 @@ export default defineNuxtComponent({
sections[sections.length - 1].ingredients.push(ingredient); sections[sections.length - 1].ingredients.push(ingredient);
return sections; return sections;
}, [] as IngredientSection[]); }, [] as IngredientSection[]);
}); });
// Group instructions by section so we can style them independently // Group instructions by section so we can style them independently
const instructionSections = computed<InstructionSection[]>(() => { const instructionSections = computed<InstructionSection[]>(() => {
if (!props.recipe.recipeInstructions) { if (!props.recipe.recipeInstructions) {
return []; return [];
} }
@ -324,31 +314,15 @@ export default defineNuxtComponent({
sections[sections.length - 1].instructions.push(step); sections[sections.length - 1].instructions.push(step);
return sections; return sections;
}, [] as InstructionSection[]); }, [] as InstructionSection[]);
});
const hasNotes = computed(() => {
return props.recipe.notes && props.recipe.notes.length > 0;
});
function parseText(ingredient: RecipeIngredient) {
return parseIngredientText(ingredient, props.recipe.settings?.disableAmount || false, props.scale);
}
return {
labels,
hasNotes,
imageKey,
ImagePosition,
parseText,
parseIngredientText,
preferences,
recipeImageUrl,
recipeYield,
ingredientSections,
instructionSections,
};
},
}); });
const hasNotes = computed(() => {
return props.recipe.notes && props.recipe.notes.length > 0;
});
function parseText(ingredient: RecipeIngredient) {
return parseIngredientText(ingredient, props.recipe.settings?.disableAmount || false, props.scale);
}
</script> </script>
<style scoped> <style scoped>