mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-24 01:12:54 -04:00
fix: recipe ingredient editor bugs (#1251)
* filter unallowed fields #1140 * fix type and layout * propery validate none type quantites * fix rendering error #1237
This commit is contained in:
parent
d06d4d2fd9
commit
cd0da36e7c
@ -20,6 +20,7 @@
|
||||
class="mx-1"
|
||||
type="number"
|
||||
placeholder="Quantity"
|
||||
@keypress="quantityFilter"
|
||||
>
|
||||
<v-icon v-if="$listeners && $listeners.delete" slot="prepend" class="mr-n1 handle">
|
||||
{{ $globals.icons.arrowUpDown }}
|
||||
@ -166,7 +167,7 @@ export default defineComponent({
|
||||
if (state.showTitle) {
|
||||
value.title = "";
|
||||
}
|
||||
state.showTitle = !state.showTitle
|
||||
state.showTitle = !state.showTitle;
|
||||
}
|
||||
|
||||
function toggleOriginalText() {
|
||||
@ -211,7 +212,15 @@ export default defineComponent({
|
||||
return options;
|
||||
});
|
||||
|
||||
function quantityFilter(e: KeyboardEvent) {
|
||||
// if digit is pressed, add to quantity
|
||||
if (e.key === "-" || e.key === "+" || e.key === "e") {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
quantityFilter,
|
||||
toggleOriginalText,
|
||||
contextMenuOptions,
|
||||
handleUnitEnter,
|
||||
|
@ -10,11 +10,8 @@
|
||||
<v-divider v-if="showTitleEditor[index]"></v-divider>
|
||||
<v-list-item dense @click="toggleChecked(index)">
|
||||
<v-checkbox hide-details :value="checked[index]" class="pt-0 my-auto py-auto" color="secondary" />
|
||||
<v-list-item-content>
|
||||
<VueMarkdown
|
||||
class="ma-0 pa-0 text-subtitle-1 dense-markdown"
|
||||
:source="parseIngredientText(ingredient, disableAmount, scale)"
|
||||
/>
|
||||
<v-list-item-content :key="ingredient.quantity">
|
||||
<VueMarkdown class="ma-0 pa-0 text-subtitle-1 dense-markdown" :source="ingredientDisplay[index]" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</div>
|
||||
@ -58,11 +55,7 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const ingredientCopyText = computed(() => {
|
||||
return props.value
|
||||
.map((ingredient) => {
|
||||
return `- [ ] ${parseIngredientText(ingredient, props.disableAmount, props.scale)}`;
|
||||
})
|
||||
.join("\n");
|
||||
return ingredientDisplay.value.join("\n");
|
||||
});
|
||||
|
||||
function toggleChecked(index: number) {
|
||||
@ -71,7 +64,14 @@ export default defineComponent({
|
||||
state.checked.splice(index, 1, !state.checked[index]);
|
||||
}
|
||||
|
||||
const ingredientDisplay = computed(() => {
|
||||
return props.value.map((ingredient) => {
|
||||
return `${parseIngredientText(ingredient, props.disableAmount, props.scale)}`;
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
ingredientDisplay,
|
||||
...toRefs(state),
|
||||
parseIngredientText,
|
||||
ingredientCopyText,
|
||||
|
@ -5,8 +5,8 @@ const { frac } = useFraction();
|
||||
|
||||
function sanitizeIngredientHTML(rawHtml: string) {
|
||||
return DOMPurify.sanitize(rawHtml, {
|
||||
"USE_PROFILES": {html: true},
|
||||
"ALLOWED_TAGS": ["b", "q", "i", "strong", "sup"]
|
||||
USE_PROFILES: { html: true },
|
||||
ALLOWED_TAGS: ["b", "q", "i", "strong", "sup"],
|
||||
});
|
||||
}
|
||||
|
||||
@ -18,7 +18,10 @@ export function parseIngredientText(ingredient: RecipeIngredient, disableAmount:
|
||||
const { quantity, food, unit, note } = ingredient;
|
||||
|
||||
let returnQty = "";
|
||||
if (quantity !== undefined && quantity !== 0) {
|
||||
|
||||
// casting to number is required as sometimes quantity is a string
|
||||
if (quantity && Number(quantity) !== 0) {
|
||||
console.log("Using Quantity", quantity, typeof quantity);
|
||||
if (unit?.fraction) {
|
||||
const fraction = frac(quantity * scale, 10, true);
|
||||
if (fraction[0] !== undefined && fraction[0] > 0) {
|
||||
|
@ -18,7 +18,7 @@
|
||||
<div class="icon-container">
|
||||
<v-divider class="icon-divider"></v-divider>
|
||||
<v-avatar class="pa-2 icon-avatar" color="primary" size="75">
|
||||
<svg class="icon-white" style="width: 75;" viewBox="0 0 24 24">
|
||||
<svg class="icon-white" style="width: 75" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z"
|
||||
/>
|
||||
@ -225,14 +225,14 @@
|
||||
<span class="headline">{{ $t("general.confirm") }}</span>
|
||||
</v-card-title>
|
||||
<v-list>
|
||||
<template v-for="(item, idx) in confirmationData.value">
|
||||
<template v-for="(item, idx) in confirmationData">
|
||||
<v-list-item v-if="item.display" :key="idx">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title> {{ item.text }} </v-list-item-title>
|
||||
<v-list-item-subtitle> {{ item.value }} </v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-divider v-if="idx !== confirmationData.value.length - 1" :key="`divider-${idx}`" />
|
||||
<v-divider v-if="idx !== confirmationData.length - 1" :key="`divider-${idx}`" />
|
||||
</template>
|
||||
</v-list>
|
||||
|
||||
@ -263,9 +263,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, useRouter, Ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { defineComponent, onMounted, ref, useRouter, Ref, useContext, computed } from "@nuxtjs/composition-api";
|
||||
import { useDark } from "@vueuse/core";
|
||||
import { computed } from "@vue/reactivity";
|
||||
import { States, RegistrationType, useRegistration } from "./states";
|
||||
import { useRouteQuery } from "~/composables/use-router";
|
||||
import { validators, useAsyncValidator } from "~/composables/use-validators";
|
||||
@ -285,7 +284,7 @@ const inputAttrs = {
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
layout: "basic",
|
||||
layout: "blank",
|
||||
setup() {
|
||||
const { i18n } = useContext();
|
||||
|
||||
|
@ -4,7 +4,7 @@ import enum
|
||||
from typing import Optional, Union
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from pydantic import UUID4, Field
|
||||
from pydantic import UUID4, Field, validator
|
||||
|
||||
from mealie.schema._mealie import MealieModel
|
||||
from mealie.schema._mealie.types import NoneFloat
|
||||
@ -53,7 +53,7 @@ class RecipeIngredient(MealieModel):
|
||||
unit: Optional[Union[IngredientUnit, CreateIngredientUnit]]
|
||||
food: Optional[Union[IngredientFood, CreateIngredientFood]]
|
||||
disable_amount: bool = True
|
||||
quantity: float = 1
|
||||
quantity: NoneFloat = 1
|
||||
original_text: Optional[str]
|
||||
|
||||
# Ref is used as a way to distinguish between an individual ingredient on the frontend
|
||||
@ -64,6 +64,20 @@ class RecipeIngredient(MealieModel):
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
@validator("quantity", pre=True)
|
||||
@classmethod
|
||||
def validate_quantity(cls, value, values) -> NoneFloat:
|
||||
"""
|
||||
Sometimes the frontend UI will provide an emptry string as a "null" value because of the default
|
||||
bindings in Vue. This validator will ensure that the quantity is set to None if the value is an
|
||||
empty string.
|
||||
"""
|
||||
if isinstance(value, float):
|
||||
return value
|
||||
if value is None or value == "":
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
class IngredientConfidence(MealieModel):
|
||||
average: NoneFloat = None
|
||||
|
Loading…
x
Reference in New Issue
Block a user