2022-05-16 01:30:33 +02:00
|
|
|
import DOMPurify from "isomorphic-dompurify";
|
2021-11-06 11:28:47 -08:00
|
|
|
import { useFraction } from "./use-fraction";
|
2025-06-20 00:09:12 +07:00
|
|
|
import type { CreateIngredientFood, CreateIngredientUnit, IngredientFood, IngredientUnit, RecipeIngredient } from "~/lib/api/types/recipe";
|
|
|
|
|
2021-11-06 11:28:47 -08:00
|
|
|
const { frac } = useFraction();
|
|
|
|
|
2023-10-07 14:23:47 -05:00
|
|
|
export function sanitizeIngredientHTML(rawHtml: string) {
|
2022-04-24 13:00:04 -08:00
|
|
|
return DOMPurify.sanitize(rawHtml, {
|
2022-05-22 11:16:23 -08:00
|
|
|
USE_PROFILES: { html: true },
|
|
|
|
ALLOWED_TAGS: ["b", "q", "i", "strong", "sup"],
|
2022-05-16 01:30:33 +02:00
|
|
|
});
|
2022-04-24 13:00:04 -08:00
|
|
|
}
|
|
|
|
|
2023-11-14 09:39:07 -06:00
|
|
|
function useFoodName(food: CreateIngredientFood | IngredientFood | undefined, usePlural: boolean) {
|
|
|
|
if (!food) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return (usePlural ? food.pluralName || food.name : food.name) || "";
|
|
|
|
}
|
|
|
|
|
|
|
|
function useUnitName(unit: CreateIngredientUnit | IngredientUnit | undefined, usePlural: boolean) {
|
|
|
|
if (!unit) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
let returnVal = "";
|
|
|
|
if (unit.useAbbreviation) {
|
|
|
|
returnVal = (usePlural ? unit.pluralAbbreviation || unit.abbreviation : unit.abbreviation) || "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!returnVal) {
|
|
|
|
returnVal = (usePlural ? unit.pluralName || unit.name : unit.name) || "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return returnVal;
|
|
|
|
}
|
|
|
|
|
2023-08-31 12:08:44 -05:00
|
|
|
export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmount: boolean, scale = 1, includeFormating = true) {
|
2021-11-06 11:28:47 -08:00
|
|
|
if (disableAmount) {
|
2023-08-21 17:32:09 +02:00
|
|
|
return {
|
|
|
|
name: ingredient.note ? sanitizeIngredientHTML(ingredient.note) : undefined,
|
|
|
|
quantity: undefined,
|
|
|
|
unit: undefined,
|
|
|
|
note: undefined,
|
|
|
|
};
|
2021-11-06 11:28:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
const { quantity, food, unit, note } = ingredient;
|
2024-08-22 10:14:32 -05:00
|
|
|
const usePluralUnit = quantity !== undefined && ((quantity || 0) * scale > 1 || (quantity || 0) * scale === 0);
|
2025-06-20 00:09:12 +07:00
|
|
|
const usePluralFood = (!quantity) || quantity * scale > 1;
|
2021-11-06 11:28:47 -08:00
|
|
|
|
|
|
|
let returnQty = "";
|
2022-05-22 11:16:23 -08:00
|
|
|
|
|
|
|
// casting to number is required as sometimes quantity is a string
|
|
|
|
if (quantity && Number(quantity) !== 0) {
|
2024-05-12 14:15:26 -05:00
|
|
|
if (unit && !unit.fraction) {
|
2024-10-30 16:12:45 +01:00
|
|
|
returnQty = Number((quantity * scale).toPrecision(3)).toString();
|
2025-06-20 00:09:12 +07:00
|
|
|
}
|
|
|
|
else {
|
2022-01-09 07:15:23 +01:00
|
|
|
const fraction = frac(quantity * scale, 10, true);
|
|
|
|
if (fraction[0] !== undefined && fraction[0] > 0) {
|
|
|
|
returnQty += fraction[0];
|
|
|
|
}
|
2021-11-06 11:28:47 -08:00
|
|
|
|
2022-01-09 07:15:23 +01:00
|
|
|
if (fraction[1] > 0) {
|
2025-06-20 00:09:12 +07:00
|
|
|
returnQty += includeFormating
|
|
|
|
? `<sup>${fraction[1]}</sup><span>⁄</span><sub>${fraction[2]}</sub>`
|
|
|
|
: ` ${fraction[1]}/${fraction[2]}`;
|
2022-01-09 07:15:23 +01:00
|
|
|
}
|
2021-11-06 11:28:47 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-22 10:14:32 -05:00
|
|
|
const unitName = useUnitName(unit || undefined, usePluralUnit);
|
|
|
|
const foodName = useFoodName(food || undefined, usePluralFood);
|
2023-11-14 09:39:07 -06:00
|
|
|
|
2023-08-21 17:32:09 +02:00
|
|
|
return {
|
|
|
|
quantity: returnQty ? sanitizeIngredientHTML(returnQty) : undefined,
|
2023-11-14 09:39:07 -06:00
|
|
|
unit: unitName && quantity ? sanitizeIngredientHTML(unitName) : undefined,
|
|
|
|
name: foodName ? sanitizeIngredientHTML(foodName) : undefined,
|
2023-08-21 17:32:09 +02:00
|
|
|
note: note ? sanitizeIngredientHTML(note) : undefined,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-08-31 12:08:44 -05:00
|
|
|
export function parseIngredientText(ingredient: RecipeIngredient, disableAmount: boolean, scale = 1, includeFormating = true): string {
|
|
|
|
const { quantity, unit, name, note } = useParsedIngredientText(ingredient, disableAmount, scale, includeFormating);
|
2023-08-21 17:32:09 +02:00
|
|
|
|
|
|
|
const text = `${quantity || ""} ${unit || ""} ${name || ""} ${note || ""}`.replace(/ {2,}/g, " ").trim();
|
2022-04-24 13:00:04 -08:00
|
|
|
return sanitizeIngredientHTML(text);
|
2021-11-06 11:28:47 -08:00
|
|
|
}
|