mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 18:23:18 -04:00 
			
		
		
		
	
		
			
	
	
		
			133 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			133 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | import { useFraction } from "~/composables/recipes"; | ||
|  | 
 | ||
|  | const matchMixedFraction = /(?:\d*\s\d*\d*|0)\/\d*\d*/; | ||
|  | const matchFraction = /(?:\d*\d*|0)\/\d*\d*/; | ||
|  | const matchDecimal = /(\d+.\d+)|(.\d+)/; | ||
|  | const matchInt = /\d+/; | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | function extractServingsFromMixedFraction(fractionString: string): number | undefined { | ||
|  |     const mixedSplit = fractionString.split(/\s/); | ||
|  |     const wholeNumber = parseInt(mixedSplit[0]); | ||
|  |     const fraction = mixedSplit[1]; | ||
|  | 
 | ||
|  |     const fractionSplit = fraction.split("/"); | ||
|  |     const numerator = parseInt(fractionSplit[0]); | ||
|  |     const denominator = parseInt(fractionSplit[1]); | ||
|  | 
 | ||
|  |     if (denominator === 0) { | ||
|  |         return undefined;  // if the denominator is zero, just give up
 | ||
|  |     } | ||
|  |     else { | ||
|  |         return wholeNumber + (numerator / denominator); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | function extractServingsFromFraction(fractionString: string): number | undefined { | ||
|  |     const fractionSplit = fractionString.split("/"); | ||
|  |     const numerator = parseInt(fractionSplit[0]); | ||
|  |     const denominator = parseInt(fractionSplit[1]); | ||
|  | 
 | ||
|  |     if (denominator === 0) { | ||
|  |         return undefined;  // if the denominator is zero, just give up
 | ||
|  |     } | ||
|  |     else { | ||
|  |         return numerator / denominator; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | function findMatch(yieldString: string): [matchString: string, servings: number, isFraction: boolean] | null { | ||
|  |     if (!yieldString) { | ||
|  |         return null; | ||
|  |     } | ||
|  | 
 | ||
|  |     const mixedFractionMatch = yieldString.match(matchMixedFraction); | ||
|  |     if (mixedFractionMatch?.length) { | ||
|  |         const match = mixedFractionMatch[0]; | ||
|  |         const servings = extractServingsFromMixedFraction(match); | ||
|  | 
 | ||
|  |         // if the denominator is zero, return no match
 | ||
|  |         if (servings === undefined) { | ||
|  |             return null; | ||
|  |         } else { | ||
|  |             return [match, servings, true]; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     const fractionMatch = yieldString.match(matchFraction); | ||
|  |     if (fractionMatch?.length) { | ||
|  |         const match = fractionMatch[0] | ||
|  |         const servings = extractServingsFromFraction(match); | ||
|  | 
 | ||
|  |         // if the denominator is zero, return no match
 | ||
|  |         if (servings === undefined) { | ||
|  |             return null; | ||
|  |         } else { | ||
|  |             return [match, servings, true]; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     const decimalMatch = yieldString.match(matchDecimal); | ||
|  |     if (decimalMatch?.length) { | ||
|  |         const match = decimalMatch[0]; | ||
|  |         return [match, parseFloat(match), false]; | ||
|  |     } | ||
|  | 
 | ||
|  |     const intMatch = yieldString.match(matchInt); | ||
|  |     if (intMatch?.length) { | ||
|  |         const match = intMatch[0]; | ||
|  |         return [match, parseInt(match), false]; | ||
|  |     } | ||
|  | 
 | ||
|  |     return null; | ||
|  | } | ||
|  | 
 | ||
|  | function formatServings(servings: number, scale: number, isFraction: boolean): string { | ||
|  |     const val = servings * scale; | ||
|  |     if (Number.isInteger(val)) { | ||
|  |         return val.toString(); | ||
|  |     } else if (!isFraction) { | ||
|  |         return (Math.round(val * 1000) / 1000).toString(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // convert val into a fraction string
 | ||
|  |     const { frac } = useFraction(); | ||
|  | 
 | ||
|  |     let valString = ""; | ||
|  |     const fraction = frac(val, 10, true); | ||
|  | 
 | ||
|  |     if (fraction[0] !== undefined && fraction[0] > 0) { | ||
|  |         valString += fraction[0]; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (fraction[1] > 0) { | ||
|  |         valString += ` ${fraction[1]}/${fraction[2]}`; | ||
|  |     } | ||
|  | 
 | ||
|  |     return valString.trim(); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | export function useExtractRecipeYield(yieldString: string | null, scale: number): string { | ||
|  |     if (!yieldString) { | ||
|  |         return ""; | ||
|  |     } | ||
|  | 
 | ||
|  |     const match = findMatch(yieldString); | ||
|  |     if (!match) { | ||
|  |         return yieldString; | ||
|  |     } | ||
|  | 
 | ||
|  |     const [matchString, servings, isFraction] = match; | ||
|  | 
 | ||
|  |     const formattedServings = formatServings(servings, scale, isFraction); | ||
|  |     if (!formattedServings) { | ||
|  |         return yieldString  // this only happens with very weird or small fractions
 | ||
|  |     } else { | ||
|  |         return yieldString.replace(matchString, formatServings(servings, scale, isFraction)); | ||
|  |     } | ||
|  | } |