mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-05-07 02:23:30 -04:00
feat: Improve add shopping list item form (#7091)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Michael Genson <genson.michael@gmail.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<v-navigation-drawer
|
||||||
|
permanent
|
||||||
|
rounded="t-xl"
|
||||||
|
location="bottom"
|
||||||
|
class="pa-4 pt-2 mb-0"
|
||||||
|
width="300"
|
||||||
|
rail-width="85"
|
||||||
|
:rail="rail"
|
||||||
|
elevation="4"
|
||||||
|
>
|
||||||
|
<div class="d-flex flex-column ga-3">
|
||||||
|
<v-card-actions class="pa-0">
|
||||||
|
<InputLabelType
|
||||||
|
v-model="listItem.food"
|
||||||
|
v-model:item-id="listItem.foodId!"
|
||||||
|
:items="foods"
|
||||||
|
:label="rail ? $t('shopping-list.add-item') : $t('shopping-list.food')"
|
||||||
|
:icon="$globals.icons.foods"
|
||||||
|
:style="rail ? 'margin-inline: 3px;' : undefined"
|
||||||
|
:search="rail"
|
||||||
|
create
|
||||||
|
@create="createAssignFood"
|
||||||
|
@focus="rail = false"
|
||||||
|
/>
|
||||||
|
<BaseButtonGroup
|
||||||
|
v-if="!rail"
|
||||||
|
:buttons="[
|
||||||
|
{
|
||||||
|
icon: $globals.icons.close,
|
||||||
|
text: $t('general.cancel'),
|
||||||
|
event: 'cancel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.save,
|
||||||
|
text: $t('general.save'),
|
||||||
|
event: 'save',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@save="$emit('save')"
|
||||||
|
@cancel="rail = true; $emit('cancel')"
|
||||||
|
/>
|
||||||
|
</v-card-actions>
|
||||||
|
|
||||||
|
<ShoppingListItemDetails
|
||||||
|
v-if="!rail"
|
||||||
|
v-model="listItem"
|
||||||
|
:labels="labels"
|
||||||
|
:units="units"
|
||||||
|
@save="$emit('save')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</v-navigation-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useShoppingListItemEditor } from "~/composables/shopping-list-page/use-shopping-list-item-editor";
|
||||||
|
import type { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
|
||||||
|
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||||
|
import type { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
|
import ShoppingListItemDetails from "./ShoppingListItemDetails.vue";
|
||||||
|
|
||||||
|
// modelValue as reactive v-model
|
||||||
|
const listItem = defineModel<ShoppingListItemCreate | ShoppingListItemOut>({ required: true });
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
labels: {
|
||||||
|
type: Array as () => MultiPurposeLabelOut[],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
units: {
|
||||||
|
type: Array as () => IngredientUnit[],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
foods: {
|
||||||
|
type: Array as () => IngredientFood[],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: "save" | "cancel" | "delete"): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { createAssignFood } = useShoppingListItemEditor(listItem);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => listItem.value.quantity,
|
||||||
|
(newQty) => {
|
||||||
|
if (!newQty) {
|
||||||
|
listItem.value.quantity = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => listItem.value.food,
|
||||||
|
(newFood) => {
|
||||||
|
listItem.value.label = newFood?.label || null;
|
||||||
|
listItem.value.labelId = listItem.value.label?.id || null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const rail = ref(true);
|
||||||
|
</script>
|
||||||
@@ -145,6 +145,7 @@
|
|||||||
:labels="labels"
|
:labels="labels"
|
||||||
:units="units"
|
:units="units"
|
||||||
:foods="foods"
|
:foods="foods"
|
||||||
|
class="ma-2"
|
||||||
@save="save"
|
@save="save"
|
||||||
@cancel="toggleEdit(false)"
|
@cancel="toggleEdit(false)"
|
||||||
@delete="$emit('delete')"
|
@delete="$emit('delete')"
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div class="d-flex ga-3">
|
||||||
|
<v-number-input
|
||||||
|
v-model="listItem.quantity"
|
||||||
|
hide-details
|
||||||
|
:label="$t('form.quantity-label-abbreviated')"
|
||||||
|
:min="0"
|
||||||
|
:precision="null"
|
||||||
|
control-variant="stacked"
|
||||||
|
style="flex: 1"
|
||||||
|
inset
|
||||||
|
/>
|
||||||
|
<InputLabelType
|
||||||
|
v-model="listItem.unit"
|
||||||
|
v-model:item-id="listItem.unitId!"
|
||||||
|
:items="units"
|
||||||
|
:label="$t('recipe.unit')"
|
||||||
|
:icon="$globals.icons.units"
|
||||||
|
style="flex: 3"
|
||||||
|
create
|
||||||
|
@create="createAssignUnit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<v-textarea
|
||||||
|
v-model="listItem.note"
|
||||||
|
hide-details
|
||||||
|
:label="$t('shopping-list.note')"
|
||||||
|
rows="1"
|
||||||
|
auto-grow
|
||||||
|
@keypress="handleNoteKeyPress"
|
||||||
|
/>
|
||||||
|
<div class="d-flex flex-wrap align-end ga-3">
|
||||||
|
<InputLabelType
|
||||||
|
v-model="listItem.label"
|
||||||
|
v-model:item-id="listItem.labelId!"
|
||||||
|
:items="labels"
|
||||||
|
:label="$t('shopping-list.label')"
|
||||||
|
style="flex: 1 0 200px"
|
||||||
|
/>
|
||||||
|
<BaseButton
|
||||||
|
v-if="listItem.labelId && listItem.food && listItem.labelId !== listItem.food.labelId"
|
||||||
|
small
|
||||||
|
color="info"
|
||||||
|
:icon="$globals.icons.tagArrowRight"
|
||||||
|
:text="$t('shopping-list.save-label')"
|
||||||
|
class="mt-2 align-items-flex-start"
|
||||||
|
style="flex-grow: 0"
|
||||||
|
@click="assignLabelToFood"
|
||||||
|
/>
|
||||||
|
<v-spacer />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useShoppingListItemEditor } from "~/composables/shopping-list-page/use-shopping-list-item-editor";
|
||||||
|
import type { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
|
||||||
|
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||||
|
import type { IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
|
// modelValue as reactive v-model
|
||||||
|
const listItem = defineModel<ShoppingListItemCreate | ShoppingListItemOut>({ required: true });
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
labels: {
|
||||||
|
type: Array as () => MultiPurposeLabelOut[],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
units: {
|
||||||
|
type: Array as () => IngredientUnit[],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{ (e: "save"): void }>();
|
||||||
|
|
||||||
|
const { assignLabelToFood, createAssignUnit } = useShoppingListItemEditor(listItem);
|
||||||
|
|
||||||
|
function handleNoteKeyPress(event: KeyboardEvent) {
|
||||||
|
// Save on Enter
|
||||||
|
if (!event.shiftKey && event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
emit("save");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,112 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<v-card variant="elevated" class="pa-2" border="primary s-lg opacity-100">
|
||||||
<v-card variant="outlined">
|
<div class="d-flex flex-column ga-3">
|
||||||
<v-card-text class="pb-3 pt-1">
|
<InputLabelType
|
||||||
<div class="d-md-flex align-center mb-2" style="gap: 20px">
|
v-model="listItem.food"
|
||||||
<div>
|
v-model:item-id="listItem.foodId!"
|
||||||
<v-number-input
|
:items="foods"
|
||||||
v-model="listItem.quantity"
|
:label="$t('shopping-list.food')"
|
||||||
hide-details
|
:icon="$globals.icons.foods"
|
||||||
:label="$t('form.quantity-label-abbreviated')"
|
:autofocus="autoFocus === 'food'"
|
||||||
:min="0"
|
create
|
||||||
:precision="null"
|
@create="createAssignFood"
|
||||||
control-variant="stacked"
|
/>
|
||||||
inset
|
<ShoppingListItemDetails
|
||||||
style="width: 100px;"
|
v-model="listItem"
|
||||||
/>
|
:labels="labels"
|
||||||
</div>
|
:units="units"
|
||||||
<InputLabelType
|
@save="$emit('save')"
|
||||||
v-model="listItem.unit"
|
/>
|
||||||
v-model:item-id="listItem.unitId!"
|
</div>
|
||||||
:items="units"
|
<v-card-actions class="justify-end pa-0">
|
||||||
:label="$t('recipe.unit')"
|
<BaseButtonGroup
|
||||||
:icon="$globals.icons.units"
|
:buttons="[
|
||||||
create
|
...(allowDelete
|
||||||
@create="createAssignUnit"
|
? [
|
||||||
/>
|
{
|
||||||
<InputLabelType
|
icon: $globals.icons.delete,
|
||||||
v-model="listItem.food"
|
text: $t('general.delete'),
|
||||||
v-model:item-id="listItem.foodId!"
|
event: 'delete',
|
||||||
:items="foods"
|
},
|
||||||
:label="$t('shopping-list.food')"
|
]
|
||||||
:icon="$globals.icons.foods"
|
: []),
|
||||||
:autofocus="autoFocus === 'food'"
|
{
|
||||||
create
|
icon: $globals.icons.close,
|
||||||
@create="createAssignFood"
|
text: $t('general.cancel'),
|
||||||
/>
|
event: 'cancel',
|
||||||
</div>
|
},
|
||||||
<div class="d-md-flex align-center" style="gap: 20px">
|
{
|
||||||
<v-textarea
|
icon: $globals.icons.save,
|
||||||
v-model="listItem.note"
|
text: $t('general.save'),
|
||||||
hide-details
|
event: 'save',
|
||||||
:label="$t('shopping-list.note')"
|
},
|
||||||
rows="1"
|
]"
|
||||||
auto-grow
|
@save="$emit('save')"
|
||||||
:autofocus="autoFocus === 'note'"
|
@cancel="$emit('cancel')"
|
||||||
@keypress="handleNoteKeyPress"
|
@delete="$emit('delete')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</v-card-actions>
|
||||||
<div class="d-flex flex-wrap align-end" style="gap: 20px">
|
</v-card>
|
||||||
<div class="d-flex align-end">
|
|
||||||
<div style="max-width: 300px" class="mt-3 mr-auto">
|
|
||||||
<InputLabelType
|
|
||||||
v-model="listItem.label"
|
|
||||||
v-model:item-id="listItem.labelId!"
|
|
||||||
:items="labels"
|
|
||||||
:label="$t('shopping-list.label')"
|
|
||||||
width="250"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<BaseButton
|
|
||||||
v-if="listItem.labelId && listItem.food && listItem.labelId !== listItem.food.labelId"
|
|
||||||
small
|
|
||||||
color="info"
|
|
||||||
:icon="$globals.icons.tagArrowRight"
|
|
||||||
:text="$t('shopping-list.save-label')"
|
|
||||||
class="mt-2 align-items-flex-start"
|
|
||||||
@click="assignLabelToFood"
|
|
||||||
/>
|
|
||||||
<v-spacer />
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions class="ma-0 pt-0 pb-1 justify-end">
|
|
||||||
<BaseButtonGroup
|
|
||||||
:buttons="[
|
|
||||||
...(allowDelete
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
icon: $globals.icons.delete,
|
|
||||||
text: $t('general.delete'),
|
|
||||||
event: 'delete',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
|
||||||
icon: $globals.icons.close,
|
|
||||||
text: $t('general.cancel'),
|
|
||||||
event: 'cancel',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: $globals.icons.save,
|
|
||||||
text: $t('general.save'),
|
|
||||||
event: 'save',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
@save="$emit('save')"
|
|
||||||
@cancel="$emit('cancel')"
|
|
||||||
@delete="$emit('delete')"
|
|
||||||
/>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useShoppingListItemEditor } from "~/composables/shopping-list-page/use-shopping-list-item-editor";
|
||||||
import type { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
|
import type { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
|
||||||
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||||
import type { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
import type { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
|
import ShoppingListItemDetails from "./ShoppingListItemDetails.vue";
|
||||||
|
|
||||||
// modelValue as reactive v-model
|
// modelValue as reactive v-model
|
||||||
const listItem = defineModel<ShoppingListItemCreate | ShoppingListItemOut>({ required: true });
|
const listItem = defineModel<ShoppingListItemCreate | ShoppingListItemOut>({ required: true });
|
||||||
@@ -132,16 +80,11 @@ defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// const emit = defineEmits<["save", "cancel", "delete"]>();
|
// const emit = defineEmits<["save", "cancel", "delete"]>();
|
||||||
const emit = defineEmits<{
|
defineEmits<{
|
||||||
(e: "save", item: ShoppingListItemOut): void;
|
(e: "save" | "cancel" | "delete"): void;
|
||||||
(e: "cancel" | "delete"): void;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const foodStore = useFoodStore();
|
const { createAssignFood } = useShoppingListItemEditor(listItem);
|
||||||
const foodData = useFoodData();
|
|
||||||
|
|
||||||
const unitStore = useUnitStore();
|
|
||||||
const unitData = useUnitData();
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => listItem.value.quantity,
|
() => listItem.value.quantity,
|
||||||
@@ -161,49 +104,4 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const autoFocus = computed(() => (!listItem.value.food && listItem.value.note ? "note" : "food"));
|
const autoFocus = computed(() => (!listItem.value.food && listItem.value.note ? "note" : "food"));
|
||||||
|
|
||||||
async function createAssignFood(val: string) {
|
|
||||||
// keep UI reactive
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
||||||
listItem.value.food ? (listItem.value.food.name = val) : (listItem.value.food = { name: val } as any);
|
|
||||||
|
|
||||||
foodData.data.name = val;
|
|
||||||
const newFood = await foodStore.actions.createOne(foodData.data);
|
|
||||||
if (newFood) {
|
|
||||||
listItem.value.food = newFood;
|
|
||||||
listItem.value.foodId = newFood.id;
|
|
||||||
}
|
|
||||||
foodData.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createAssignUnit(val: string) {
|
|
||||||
// keep UI reactive
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
||||||
listItem.value.unit ? (listItem.value.unit.name = val) : (listItem.value.unit = { name: val } as any);
|
|
||||||
|
|
||||||
unitData.data.name = val;
|
|
||||||
const newUnit = await unitStore.actions.createOne(unitData.data);
|
|
||||||
if (newUnit) {
|
|
||||||
listItem.value.unit = newUnit;
|
|
||||||
listItem.value.unitId = newUnit.id;
|
|
||||||
}
|
|
||||||
unitData.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function assignLabelToFood() {
|
|
||||||
if (!(listItem.value.food && listItem.value.foodId && listItem.value.labelId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
listItem.value.food.labelId = listItem.value.labelId;
|
|
||||||
await foodStore.actions.updateOne(listItem.value.food);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleNoteKeyPress(event: KeyboardEvent) {
|
|
||||||
const e = event as KeyboardEvent & { key: string; shiftKey: boolean };
|
|
||||||
if (!e.shiftKey && e.key === "Enter") {
|
|
||||||
e.preventDefault();
|
|
||||||
emit("save");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,12 +7,15 @@
|
|||||||
item-title="name"
|
item-title="name"
|
||||||
return-object
|
return-object
|
||||||
:items="filteredItems"
|
:items="filteredItems"
|
||||||
:prepend-icon="icon || $globals.icons.tags"
|
:prepend-inner-icon="icon || (search ? $globals.icons.search : $globals.icons.tags)"
|
||||||
|
:menu-icon="search ? '' : undefined"
|
||||||
|
:rounded="search ? true : '4px'"
|
||||||
|
:custom-filter="() => true"
|
||||||
|
:variant="search ? 'solo-filled' : undefined"
|
||||||
|
color="primary"
|
||||||
auto-select-first
|
auto-select-first
|
||||||
clearable
|
clearable
|
||||||
color="primary"
|
|
||||||
hide-details
|
hide-details
|
||||||
:custom-filter="() => true"
|
|
||||||
@keyup.enter="emitCreate"
|
@keyup.enter="emitCreate"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
@@ -55,6 +58,10 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
search: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import type { ModelRef } from "vue";
|
||||||
|
import type { ShoppingListItemOut, ShoppingListItemCreate } from "~/lib/api/types/household";
|
||||||
|
import { useFoodData, useFoodStore, useUnitData, useUnitStore } from "../store";
|
||||||
|
|
||||||
|
export function useShoppingListItemEditor(listItem: ModelRef<ShoppingListItemOut | ShoppingListItemCreate, string, ShoppingListItemOut | ShoppingListItemCreate, ShoppingListItemOut | ShoppingListItemCreate>) {
|
||||||
|
const foodStore = useFoodStore();
|
||||||
|
const foodData = useFoodData();
|
||||||
|
|
||||||
|
const unitStore = useUnitStore();
|
||||||
|
const unitData = useUnitData();
|
||||||
|
|
||||||
|
async function createAssignFood(val: string) {
|
||||||
|
// keep UI reactive
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
listItem.value.food ? (listItem.value.food.name = val) : (listItem.value.food = { name: val } as any);
|
||||||
|
|
||||||
|
foodData.data.name = val;
|
||||||
|
const newFood = await foodStore.actions.createOne(foodData.data);
|
||||||
|
if (newFood) {
|
||||||
|
listItem.value.food = newFood;
|
||||||
|
listItem.value.foodId = newFood.id;
|
||||||
|
}
|
||||||
|
foodData.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createAssignUnit(val: string) {
|
||||||
|
// keep UI reactive
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
listItem.value.unit ? (listItem.value.unit.name = val) : (listItem.value.unit = { name: val } as any);
|
||||||
|
|
||||||
|
unitData.data.name = val;
|
||||||
|
const newUnit = await unitStore.actions.createOne(unitData.data);
|
||||||
|
if (newUnit) {
|
||||||
|
listItem.value.unit = newUnit;
|
||||||
|
listItem.value.unitId = newUnit.id;
|
||||||
|
}
|
||||||
|
unitData.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assignLabelToFood() {
|
||||||
|
if (!(listItem.value.food && listItem.value.foodId && listItem.value.labelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listItem.value.food.labelId = listItem.value.labelId;
|
||||||
|
await foodStore.actions.updateOne(listItem.value.food);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
assignLabelToFood,
|
||||||
|
createAssignFood,
|
||||||
|
createAssignUnit,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -916,6 +916,7 @@
|
|||||||
"quantity": "Quantity: {0}",
|
"quantity": "Quantity: {0}",
|
||||||
"shopping-list": "Shopping List",
|
"shopping-list": "Shopping List",
|
||||||
"shopping-lists": "Shopping Lists",
|
"shopping-lists": "Shopping Lists",
|
||||||
|
"add-item": "Add item",
|
||||||
"food": "Food",
|
"food": "Food",
|
||||||
"note": "Note",
|
"note": "Note",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
|
|||||||
@@ -160,8 +160,20 @@
|
|||||||
<!-- Viewer -->
|
<!-- Viewer -->
|
||||||
<section v-if="!edit" class="py-2 d-flex flex-column ga-4">
|
<section v-if="!edit" class="py-2 d-flex flex-column ga-4">
|
||||||
<!-- Create Item -->
|
<!-- Create Item -->
|
||||||
<div v-if="createEditorOpen">
|
<ShoppingListAddItemForm
|
||||||
|
v-if="$vuetify.display.smAndDown"
|
||||||
|
v-model="createListItemData"
|
||||||
|
class="my-4"
|
||||||
|
:labels="allLabels || []"
|
||||||
|
:units="allUnits || []"
|
||||||
|
:foods="allFoods || []"
|
||||||
|
@cancel="createEditorOpen = false"
|
||||||
|
@save="createListItem"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
<ShoppingListItemEditor
|
<ShoppingListItemEditor
|
||||||
|
v-if="createEditorOpen"
|
||||||
v-model="createListItemData"
|
v-model="createListItemData"
|
||||||
class="my-4"
|
class="my-4"
|
||||||
:labels="allLabels || []"
|
:labels="allLabels || []"
|
||||||
@@ -172,14 +184,14 @@
|
|||||||
@cancel="createEditorOpen = false"
|
@cancel="createEditorOpen = false"
|
||||||
@save="createListItem"
|
@save="createListItem"
|
||||||
/>
|
/>
|
||||||
</div>
|
<InputLabelType
|
||||||
<div v-else class="d-flex justify-end">
|
v-else
|
||||||
<BaseButton
|
:items="allFoods"
|
||||||
create
|
:label="$t('shopping-list.add-item')"
|
||||||
@click="createEditorOpen = true"
|
:icon="$globals.icons.foods"
|
||||||
>
|
search
|
||||||
{{ $t('general.add') }}
|
@focus="createEditorOpen = true"
|
||||||
</BaseButton>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TransitionGroup name="scroll-x-transition">
|
<TransitionGroup name="scroll-x-transition">
|
||||||
@@ -338,6 +350,7 @@
|
|||||||
import { VueDraggable } from "vue-draggable-plus";
|
import { VueDraggable } from "vue-draggable-plus";
|
||||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||||
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue";
|
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue";
|
||||||
|
import ShoppingListAddItemForm from "~/components/Domain/ShoppingList/ShoppingListAddItemForm.vue";
|
||||||
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
||||||
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
||||||
import { useShoppingListPage } from "~/composables/shopping-list-page/use-shopping-list-page";
|
import { useShoppingListPage } from "~/composables/shopping-list-page/use-shopping-list-page";
|
||||||
|
|||||||
Reference in New Issue
Block a user