Skip to content

MealReviewCard

The card shell around a "confirm/edit this logged meal" screen — a name field, an optional quantity hint, and slots for the portion/gram + macro stepper controls (yemek's own NumberStepper), used before an AI recognition result or a search-picked product is actually saved.

Playground

Werte überprüfen

1 Portion ≈ 350g

Click "Als verzehrt markieren" — a checkmark pulses immediately, then the button shows a spinner (simulating a ~1.2s save call) before returning to normal. Toggle off the quantity slot to see the search-picked-product variant (no portion baseline). The steppers here are the real NumberStepper component, not placeholders — try nudging a value.

vue
<script setup>
import { MealReviewCard, NumberStepper } from 'yemek'

const confirmName = ref('...')
const saving = ref(false)

async function saveMeal() {
  saving.value = true
  await supabase.from('meals').insert({ /* ... */ })
  saving.value = false
}
</script>

<template>
  <MealReviewCard
    title="Werte überprüfen"
    v-model:name="confirmName"
    name-label="Bezeichnung"
    :quantity-hint="`1 Portion ≈ ${basePortionGrams}g`"
    confirm-label="Als verzehrt markieren"
    :saving="saving"
    @confirm="saveMeal"
  >
    <template #quantity>
      <div class="grid-2">
        <NumberStepper v-model="portionCount" :min="0.25" :max="20" :step="0.5" @update:model-value="updateFromPortions" />
        <NumberStepper v-model="gramsAmount" :step-percent="0.1" :min="0" :max="9999" @update:model-value="updateFromGrams" />
      </div>
    </template>
    <template #macros>
      <div class="grid-2">
        <NumberStepper v-model="confirmCalories" accent-color="var(--yemek-nutrient-calories)" :min="0" :max="4000" />
        <NumberStepper v-model="confirmProtein" accent-color="var(--yemek-nutrient-protein)" :min="0" :max="300" />
        <NumberStepper v-model="confirmCarbs" accent-color="var(--yemek-nutrient-carbs)" :min="0" :max="500" />
        <NumberStepper v-model="confirmFat" accent-color="var(--yemek-nutrient-fat)" :min="0" :max="300" />
      </div>
    </template>
  </MealReviewCard>
</template>

Props

PropTypeDefaultDescription
titlestringCard heading.
namestringMeal name (v-model).
nameLabelstringAlready-localized label above the name field.
quantityHintstringundefinedAlready-formatted, e.g. "1 Portion ≈ 350g". Omit for a search-picked product with no portion baseline — the #quantity slot section then also doesn't render.
confirmLabelstringConfirm button label.
savingbooleanfalseDisables the button and shows a spinner while the consumer's save call is in flight.

Slots

SlotDescription
quantityThe portion/gram stepper controls (only rendered — including the quantityHint line — when this slot has content). Typically yemek's own NumberStepper.
macrosThe calorie/protein/carbs/fat stepper controls. Rendered with display: contents, so the consumer's own grid wrapper fully controls layout.

Events

EventPayloadFired when
update:namestringThe name field changes (standard v-model).
confirmThe confirm button is clicked (not fired while saving is true).

Behavior notes

  • No portion/gram scaling math and no macro clamping live in the component — those stay the consumer's domain logic (in meal-tracker's case, app/lib/portions.ts/mealLimits.ts). The slots hold the actual stepper controls — yemek's own NumberStepper, or any other input the consumer prefers.
  • Clicking confirm triggers an immediate checkmark-pulse micro-interaction (500ms) before saving's spinner (if the consumer sets it) takes over — feedback that the click registered, replacing a plain button with zero click feedback.
  • prefers-reduced-motion: reduce disables the checkmark-pulse/pop animations and slows the spinner's rotation instead of stopping it entirely (it's a loading indicator, not decorative).
  • The quantity panel's background is derived relative to the card's own surface (color-mix(in srgb, var(--yemek-color-text) 5%, var(--yemek-color-surface))), not the separate -inset token — same lesson as ChoicePicker's #39 fix, guarantees the panel reads as visibly distinct regardless of how close a host theme's -surface/-inset pair happen to be.
  • The confirm button uses --yemek-radius-sm (sharper), not -md — flagged as reading too rounded/pill-like next to the rest of the framework's more angular buttons.