Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 34 additions & 21 deletions playground/app/components/recipe/IngredientItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ function isAndGroup(
}

/**
* Type guard: checks if entry is a simple group with groupQuantity
* Type guard: checks if entry is a simple group with a single quantity
*/
function isSimpleGroup(
entry: IngredientQuantityEntry,
): entry is IngredientQuantityGroup {
return "groupQuantity" in entry;
return "quantity" in entry;
}

/**
Expand Down Expand Up @@ -126,9 +126,7 @@ const displayMode = computed<DisplayMode>(() => {
<template v-if="displayMode.type === 'single'">
{{ optionalPrefix }}
<span>
<RecipeQuantityWithEquivalents
:quantity="displayMode.entry.groupQuantity"
/>
<RecipeQuantityWithEquivalents :quantity="displayMode.entry.quantity" />
</span>
{{ " " }}
<span class="font-bold">{{ ingredient.name }}</span>
Expand All @@ -144,11 +142,11 @@ const displayMode = computed<DisplayMode>(() => {
<template v-else-if="displayMode.type === 'two-plain'">
<span>
<RecipeQuantityWithEquivalents
:quantity="displayMode.entries[0].groupQuantity"
:quantity="displayMode.entries[0].quantity"
/>
and
<RecipeQuantityWithEquivalents
:quantity="displayMode.entries[1].groupQuantity"
:quantity="displayMode.entries[1].quantity"
/>
</span>
{{ " " }}
Expand All @@ -169,7 +167,11 @@ const displayMode = computed<DisplayMode>(() => {
<span>
<template v-for="(qty, idx) in displayMode.entry.entries" :key="idx">
<template v-if="idx > 0"> + </template>
<RecipeQuantityWithEquivalents :quantity="qty" />
<RecipeQuantityWithEquivalents
:quantity="qty.quantity"
:unit="qty.unit"
:equivalents="qty.equivalents"
/>
</template>
<template v-if="displayMode.entry.equivalents?.length">
(≈
Expand All @@ -190,7 +192,7 @@ const displayMode = computed<DisplayMode>(() => {
<template v-else-if="isSimpleGroup(displayMode.entry)">
<span>
<RecipeQuantityWithEquivalents
:quantity="displayMode.entry.groupQuantity"
:quantity="displayMode.entry.quantity"
/>
</span>
</template>
Expand All @@ -209,14 +211,13 @@ const displayMode = computed<DisplayMode>(() => {
:key="idx"
>
<template v-if="idx > 0">, or </template>
<template v-if="alt.alternativeQuantities?.length">
<template
v-for="(altQty, qtyIdx) in alt.alternativeQuantities"
:key="qtyIdx"
>
<template v-if="alt.quantities?.length">
<template v-for="(altQty, qtyIdx) in alt.quantities" :key="qtyIdx">
<template v-if="qtyIdx > 0"> + </template>
<RecipeQuantityWithEquivalents
:quantity="altQty"
:quantity="altQty.quantity"
:unit="altQty.unit"
:equivalents="altQty.equivalents"
wrapper-start="["
wrapper-end="]"
/>
Expand All @@ -234,7 +235,11 @@ const displayMode = computed<DisplayMode>(() => {
<span>
<template v-for="(qty, idx) in displayMode.entry.entries" :key="idx">
<template v-if="idx > 0"> + </template>
<RecipeQuantityWithEquivalents :quantity="qty" />
<RecipeQuantityWithEquivalents
:quantity="qty.quantity"
:unit="qty.unit"
:equivalents="qty.equivalents"
/>
</template>
<template v-if="displayMode.entry.equivalents?.length">
{{ " " }}(≈
Expand Down Expand Up @@ -271,7 +276,11 @@ const displayMode = computed<DisplayMode>(() => {
<span>
<template v-for="(qty, qtyIdx) in entry.entries" :key="qtyIdx">
<template v-if="qtyIdx > 0"> + </template>
<RecipeQuantityWithEquivalents :quantity="qty" />
<RecipeQuantityWithEquivalents
:quantity="qty.quantity"
:unit="qty.unit"
:equivalents="qty.equivalents"
/>
</template>
<template v-if="entry.equivalents?.length">
{{ " " }}(≈
Expand All @@ -288,7 +297,7 @@ const displayMode = computed<DisplayMode>(() => {
<!-- Render simple group entry -->
<template v-else-if="isSimpleGroup(entry)">
<span>
<RecipeQuantityWithEquivalents :quantity="entry.groupQuantity" />
<RecipeQuantityWithEquivalents :quantity="entry.quantity" />
</span>
</template>

Expand All @@ -308,13 +317,17 @@ const displayMode = computed<DisplayMode>(() => {
:key="altIdx"
>
<template v-if="altIdx > 0">, </template>
<template v-if="alt.alternativeQuantities?.length">
<template v-if="alt.quantities?.length">
<template
v-for="(altQty, qtyIdx) in alt.alternativeQuantities"
v-for="(altQty, qtyIdx) in alt.quantities"
:key="qtyIdx"
>
<template v-if="qtyIdx > 0"> + </template>
<RecipeQuantityWithEquivalents :quantity="altQty" />
<RecipeQuantityWithEquivalents
:quantity="altQty.quantity"
:unit="altQty.unit"
:equivalents="altQty.equivalents"
/>
</template>
{{ " " }}
</template>
Expand Down
14 changes: 9 additions & 5 deletions playground/app/components/recipe/QuantityWithEquivalents.vue
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
<script setup lang="ts">
import type { QuantityWithPlainUnit } from "cooklang-parser";
import type { FixedValue, Range, QuantityWithPlainUnit } from "cooklang-parser";

const props = withDefaults(
defineProps<{
quantity: QuantityWithPlainUnit;
quantity: FixedValue | Range;
unit?: string;
equivalents?: QuantityWithPlainUnit[];
wrapperStart?: string;
wrapperEnd?: string;
}>(),
{
unit: undefined,
equivalents: undefined,
wrapperStart: "(",
wrapperEnd: ")",
},
);

const hasEquivalents = computed(
() => props.quantity.equivalents && props.quantity.equivalents.length > 0,
() => props.equivalents && props.equivalents.length > 0,
);
</script>

<template>
<span class="quantity-with-equivalents">
<RecipeSingleQuantity :quantity="quantity.quantity" :unit="quantity.unit" />
<RecipeSingleQuantity :quantity="quantity" :unit="unit" />
<template v-if="hasEquivalents"
>{{ " " }}{{ wrapperStart
}}<template v-for="(equiv, index) in quantity.equivalents" :key="index"
}}<template v-for="(equiv, index) in equivalents" :key="index"
><template v-if="index > 0">, </template
><RecipeSingleQuantity
:quantity="equiv.quantity"
Expand Down
30 changes: 13 additions & 17 deletions playground/app/components/recipe/StepContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,15 @@ const props = defineProps<{
}>();

/**
* Convert an IngredientItemQuantity to a QuantityWithPlainUnit for the component
* Convert IngredientItemQuantity equivalents to QuantityWithPlainUnit array
*/
function toQuantityWithPlainUnit(
function toPlainEquivalents(
itemQty: IngredientItemQuantity,
): QuantityWithPlainUnit {
return {
quantity: itemQty.quantity,
unit: itemQty.unit?.name,
equivalents: itemQty.equivalents?.map((eq) => ({
quantity: eq.quantity,
unit: eq.unit?.name,
})),
};
): QuantityWithPlainUnit[] | undefined {
return itemQty.equivalents?.map((eq) => ({
quantity: eq.quantity,
unit: eq.unit?.name,
}));
}

/**
Expand Down Expand Up @@ -79,11 +75,9 @@ function getTimer(index: number): Timer | undefined {
<!-- Primary ingredient with quantity -->
<template v-if="getPrimaryAlternative(item)?.itemQuantity">
<RecipeQuantityWithEquivalents
:quantity="
toQuantityWithPlainUnit(
getPrimaryAlternative(item)!.itemQuantity!,
)
"
:quantity="getPrimaryAlternative(item)!.itemQuantity!.quantity"
:unit="getPrimaryAlternative(item)!.itemQuantity!.unit?.name"
:equivalents="toPlainEquivalents(getPrimaryAlternative(item)!.itemQuantity!)"
/>
{{ " " }}
</template>
Expand All @@ -100,7 +94,9 @@ function getTimer(index: number): Timer | undefined {
<template v-if="altIdx > 0">, or </template>
<template v-if="alt.itemQuantity">
<RecipeQuantityWithEquivalents
:quantity="toQuantityWithPlainUnit(alt.itemQuantity)"
:quantity="alt.itemQuantity.quantity"
:unit="alt.itemQuantity.unit?.name"
:equivalents="toPlainEquivalents(alt.itemQuantity)"
wrapper-start="["
wrapper-end="]"
/>
Expand Down
21 changes: 9 additions & 12 deletions src/classes/recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ export class Recipe {
index: otherAlt.index,
};
if (otherAlt.itemQuantity) {
// Build the alternativeQuantities with plain units
// Build the alternative quantities with plain units
const altQty: QuantityWithPlainUnit = {
quantity: otherAlt.itemQuantity.quantity,
};
Expand All @@ -628,7 +628,7 @@ export class Recipe {
(eq) => toPlainUnit(eq) as QuantityWithPlainUnit,
);
}
newRef.alternativeQuantities = [altQty];
newRef.quantities = [altQty];
}
alternativeRefs.push(newRef);
}
Expand All @@ -643,7 +643,7 @@ export class Recipe {
const otherAlt = groupAlternatives[j] as IngredientAlternative;
/* v8 ignore else -- @preserve */
if (otherAlt.itemQuantity) {
// Build the alternativeQuantities with plain units
// Build the alternative quantities with plain units
const altQty: QuantityWithPlainUnit = {
quantity: otherAlt.itemQuantity.quantity,
};
Expand All @@ -657,7 +657,7 @@ export class Recipe {
}
alternativeRefs.push({
index: otherAlt.index,
alternativeQuantities: [altQty],
quantities: [altQty],
});
}
}
Expand Down Expand Up @@ -705,11 +705,8 @@ export class Recipe {
group.alternativeQuantities.set(ref.index, []);
}

if (
ref.alternativeQuantities &&
ref.alternativeQuantities.length > 0
) {
for (const altQty of ref.alternativeQuantities) {
if (ref.quantities && ref.quantities.length > 0) {
for (const altQty of ref.quantities) {
if (altQty.equivalents && altQty.equivalents.length > 0) {
const entries: QuantityWithExtendedUnit[] = [
toExtendedUnit({
Expand Down Expand Up @@ -768,9 +765,9 @@ export class Recipe {
// Convert to array of QuantityWithPlainUnit
const flattenedAlt = flattenPlainUnitGroup(summedAltQuantity);
// Extract quantities from the flattened result
ref.alternativeQuantities = flattenedAlt.flatMap((item) => {
if ("groupQuantity" in item) {
return [item.groupQuantity];
ref.quantities = flattenedAlt.flatMap((item) => {
if ("quantity" in item) {
return [item];
} else {
// AND group: return entries (could also include equivalents if needed)
return item.entries;
Expand Down
16 changes: 6 additions & 10 deletions src/quantities/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export function deNormalizeQuantity(
export const flattenPlainUnitGroup = (
summed: QuantityWithPlainUnit | MaybeNestedGroup<QuantityWithPlainUnit>,
): (
| { groupQuantity: QuantityWithPlainUnit }
| QuantityWithPlainUnit
| {
type: "and";
entries: QuantityWithPlainUnit[];
Expand Down Expand Up @@ -319,7 +319,7 @@ export const flattenPlainUnitGroup = (
];
} else {
// No equivalents: flatten to separate entries (shouldn't happen in this branch, but handle it)
return andEntries.map((entry) => ({ groupQuantity: entry }));
return andEntries;
}
}

Expand All @@ -336,14 +336,12 @@ export const flattenPlainUnitGroup = (
if (simpleEntries.length > 1) {
result.equivalents = simpleEntries.slice(1);
}
return [{ groupQuantity: result }];
return [result];
}
// Fallback: use first entry regardless
else {
const first = entries[0] as QuantityWithPlainUnit;
return [
{ groupQuantity: { quantity: first.quantity, unit: first.unit } },
];
return [{ quantity: first.quantity, unit: first.unit }];
}
} else if (isGroup(summed)) {
// AND group: check if entries have OR groups (equivalents that can be extracted)
Expand Down Expand Up @@ -378,7 +376,7 @@ export const flattenPlainUnitGroup = (
// If there are equivalents, return an AND group with the summed equivalents (carrots case)
if (equivalentsList.length === 0) {
// No equivalents: flatten to separate entries
return andEntries.map((entry) => ({ groupQuantity: entry }));
return andEntries;
}

const result: {
Expand All @@ -394,8 +392,6 @@ export const flattenPlainUnitGroup = (
return [result];
} else {
// Simple QuantityWithPlainUnit
return [
{ groupQuantity: { quantity: summed.quantity, unit: summed.unit } },
];
return [{ quantity: summed.quantity, unit: summed.unit }];
}
};
10 changes: 2 additions & 8 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export interface AlternativeIngredientRef {
/** The index of the alternative ingredient within the {@link Recipe.ingredients} array. */
index: number;
/** The quantities of the alternative ingredient. Multiple entries when units are incompatible. */
alternativeQuantities?: QuantityWithPlainUnit[];
quantities?: QuantityWithPlainUnit[];
}

/**
Expand All @@ -236,18 +236,12 @@ export interface AlternativeIngredientRef {
* When units are incompatible, separate IngredientQuantityGroup entries are created instead of merging.
* @category Types
*/
export interface IngredientQuantityGroup {
export interface IngredientQuantityGroup extends QuantityWithPlainUnit {
/**
* References to alternative ingredients for this quantity group.
* If undefined, this group has no alternatives.
*/
alternatives?: AlternativeIngredientRef[];
/**
* The summed quantity for this group, potentially with equivalents.
* OR groups from addEquivalentsAndSimplify are converted back to QuantityWithPlainUnit
* (first entry as main, rest as equivalents).
*/
groupQuantity: QuantityWithPlainUnit;
}

/**
Expand Down
Loading