mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-24 03:05:12 -05:00
feat: Replace number inputs with new v-number-input compontent (#6767)
This commit is contained in:
@@ -144,11 +144,13 @@
|
||||
variant="underlined"
|
||||
@update:model-value="setFieldValue(field, index, $event)"
|
||||
/>
|
||||
<v-text-field
|
||||
<v-number-input
|
||||
v-else-if="field.type === 'number'"
|
||||
:model-value="field.value"
|
||||
type="number"
|
||||
variant="underlined"
|
||||
control-variant="stacked"
|
||||
inset
|
||||
:precision="null"
|
||||
@update:model-value="setFieldValue(field, index, $event)"
|
||||
/>
|
||||
<v-checkbox
|
||||
|
||||
@@ -22,12 +22,15 @@
|
||||
cols="12"
|
||||
class="flex-grow-0 flex-shrink-0"
|
||||
>
|
||||
<v-text-field
|
||||
<v-number-input
|
||||
v-model="model.quantity"
|
||||
variant="solo"
|
||||
:precision="null"
|
||||
:min="0"
|
||||
hide-details
|
||||
control-variant="stacked"
|
||||
inset
|
||||
density="compact"
|
||||
type="number"
|
||||
:placeholder="$t('recipe.quantity')"
|
||||
@keypress="quantityFilter"
|
||||
>
|
||||
@@ -38,7 +41,7 @@
|
||||
{{ $globals.icons.arrowUpDown }}
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-number-input>
|
||||
</v-col>
|
||||
<v-col
|
||||
v-if="!state.isRecipe"
|
||||
|
||||
@@ -10,14 +10,17 @@
|
||||
v-for="(item, key, index) in modelValue"
|
||||
:key="index"
|
||||
>
|
||||
<v-text-field
|
||||
density="compact"
|
||||
<v-number-input
|
||||
:model-value="modelValue[key]"
|
||||
:label="labels[key].label"
|
||||
:suffix="labels[key].suffix"
|
||||
type="number"
|
||||
density="compact"
|
||||
autocomplete="off"
|
||||
variant="underlined"
|
||||
control-variant="stacked"
|
||||
inset
|
||||
:precision="null"
|
||||
:min="0"
|
||||
@update:model-value="updateValue(key, $event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -11,27 +11,27 @@
|
||||
<v-container class="ma-0 pa-0">
|
||||
<v-row>
|
||||
<v-col cols="3">
|
||||
<v-text-field
|
||||
:model-value="recipeServings"
|
||||
type="number"
|
||||
<v-number-input
|
||||
:model-value="recipe.recipeServings"
|
||||
:min="0"
|
||||
hide-spin-buttons
|
||||
:precision="null"
|
||||
density="compact"
|
||||
:label="$t('recipe.servings')"
|
||||
variant="underlined"
|
||||
@update:model-value="validateInput($event, 'recipeServings')"
|
||||
control-variant="hidden"
|
||||
@update:model-value="recipe.recipeServings = $event"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<v-text-field
|
||||
:model-value="recipeYieldQuantity"
|
||||
type="number"
|
||||
<v-number-input
|
||||
:model-value="recipe.recipeYieldQuantity"
|
||||
:min="0"
|
||||
hide-spin-buttons
|
||||
:precision="null"
|
||||
density="compact"
|
||||
:label="$t('recipe.yield')"
|
||||
variant="underlined"
|
||||
@update:model-value="validateInput($event, 'recipeYieldQuantity')"
|
||||
control-variant="hidden"
|
||||
@update:model-value="recipe.recipeYieldQuantity = $event"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
@@ -85,37 +85,4 @@ import type { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||
import type { Recipe } from "~/lib/api/types/recipe";
|
||||
|
||||
const recipe = defineModel<NoUndefinedField<Recipe>>({ required: true });
|
||||
|
||||
const recipeServings = computed<number>({
|
||||
get() {
|
||||
return recipe.value.recipeServings;
|
||||
},
|
||||
set(val) {
|
||||
validateInput(val.toString(), "recipeServings");
|
||||
},
|
||||
});
|
||||
|
||||
const recipeYieldQuantity = computed<number>({
|
||||
get() {
|
||||
return recipe.value.recipeYieldQuantity;
|
||||
},
|
||||
set(val) {
|
||||
validateInput(val.toString(), "recipeYieldQuantity");
|
||||
},
|
||||
});
|
||||
|
||||
function validateInput(value: string | null, property: "recipeServings" | "recipeYieldQuantity") {
|
||||
if (!value) {
|
||||
recipe.value[property] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const number = parseFloat(value.replace(/[^0-9.]/g, ""));
|
||||
if (isNaN(number) || number <= 0) {
|
||||
recipe.value[property] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
recipe.value[property] = number;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -65,13 +65,13 @@
|
||||
</v-card-title>
|
||||
<v-card-text class="mt-n5">
|
||||
<div class="mt-4 d-flex align-center">
|
||||
<v-text-field
|
||||
<v-number-input
|
||||
:model-value="yieldQuantity"
|
||||
type="number"
|
||||
:precision="null"
|
||||
:min="0"
|
||||
variant="underlined"
|
||||
hide-spin-buttons
|
||||
@update:model-value="recalculateScale(parseFloat($event) || 0)"
|
||||
control-variant="hidden"
|
||||
@update:model-value="recalculateScale($event || 0)"
|
||||
/>
|
||||
<v-tooltip
|
||||
location="end"
|
||||
|
||||
@@ -4,7 +4,19 @@
|
||||
<v-card-text class="pb-3 pt-1">
|
||||
<div class="d-md-flex align-center mb-2" style="gap: 20px">
|
||||
<div>
|
||||
<InputQuantity v-model="listItem.quantity" />
|
||||
<v-number-input
|
||||
v-model="listItem.quantity"
|
||||
hide-details
|
||||
:label="$t('form.quantity-label-abbreviated')"
|
||||
:min="0"
|
||||
:precision="null"
|
||||
variant="plain"
|
||||
control-variant="stacked"
|
||||
inset
|
||||
density="compact"
|
||||
class="centered-number-input"
|
||||
style="width: 100px;"
|
||||
/>
|
||||
</div>
|
||||
<InputLabelType
|
||||
v-model="listItem.unit"
|
||||
@@ -158,6 +170,15 @@ export default defineNuxtComponent({
|
||||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.modelValue.quantity,
|
||||
() => {
|
||||
if (!props.modelValue.quantity) {
|
||||
listItem.value.quantity = 0;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.modelValue.food,
|
||||
(newFood) => {
|
||||
@@ -221,3 +242,10 @@ export default defineNuxtComponent({
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.centered-number-input :deep(.v-field) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="d-flex align-center"
|
||||
style="max-width: 60px"
|
||||
>
|
||||
<v-text-field
|
||||
v-model.number="quantity"
|
||||
hide-details
|
||||
:label="$t('form.quantity-label-abbreviated')"
|
||||
:min="min"
|
||||
:max="max"
|
||||
type="number"
|
||||
variant="plain"
|
||||
density="compact"
|
||||
style="width: 60px;"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineNuxtComponent({
|
||||
name: "VInputNumber",
|
||||
props: {
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
default: 9999,
|
||||
},
|
||||
rules: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
step: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
modelValue: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, context) {
|
||||
const quantity = computed({
|
||||
get: () => {
|
||||
return Number(props.modelValue);
|
||||
},
|
||||
set: (val) => {
|
||||
context.emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
quantity,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -34,7 +34,7 @@
|
||||
"vue-advanced-cropper": "^2.8.9",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vuetify": "^3.9.7",
|
||||
"vuetify-nuxt-module": "^0.18.8"
|
||||
"vuetify-nuxt-module": "^0.18.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint": "^1.2.0",
|
||||
|
||||
@@ -134,18 +134,22 @@
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<div>
|
||||
<v-text-field
|
||||
<v-number-input
|
||||
v-model="settings.maxMissingFoods"
|
||||
type="number"
|
||||
:precision="null"
|
||||
:min="0"
|
||||
control-variant="stacked"
|
||||
inset
|
||||
hide-details
|
||||
hide-spin-buttons
|
||||
:label="$t('recipe-finder.max-missing-ingredients')"
|
||||
/>
|
||||
<v-text-field
|
||||
<v-number-input
|
||||
v-model="settings.maxMissingTools"
|
||||
type="number"
|
||||
:precision="null"
|
||||
:min="0"
|
||||
control-variant="stacked"
|
||||
inset
|
||||
hide-details
|
||||
hide-spin-buttons
|
||||
:label="$t('recipe-finder.max-missing-tools')"
|
||||
class="mt-4"
|
||||
/>
|
||||
|
||||
@@ -30,9 +30,11 @@
|
||||
/>
|
||||
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
<v-number-input
|
||||
v-model="numberOfDays"
|
||||
type="number"
|
||||
:min="1"
|
||||
control-variant="stacked"
|
||||
inset
|
||||
:label="$t('meal-plan.numberOfDays-label')"
|
||||
:hint="$t('meal-plan.numberOfDays-hint')"
|
||||
persistent-hint
|
||||
|
||||
2
frontend/types/components.d.ts
vendored
2
frontend/types/components.d.ts
vendored
@@ -27,7 +27,6 @@ import type HelpIcon from "@/components/global/HelpIcon.vue";
|
||||
import type ImageCropper from "@/components/global/ImageCropper.vue";
|
||||
import type InputColor from "@/components/global/InputColor.vue";
|
||||
import type InputLabelType from "@/components/global/InputLabelType.vue";
|
||||
import type InputQuantity from "@/components/global/InputQuantity.vue";
|
||||
import type LanguageDialog from "@/components/global/LanguageDialog.vue";
|
||||
import type MarkdownEditor from "@/components/global/MarkdownEditor.vue";
|
||||
import type RecipeJsonEditor from "@/components/global/RecipeJsonEditor.vue";
|
||||
@@ -69,7 +68,6 @@ declare module "vue" {
|
||||
ImageCropper: typeof ImageCropper;
|
||||
InputColor: typeof InputColor;
|
||||
InputLabelType: typeof InputLabelType;
|
||||
InputQuantity: typeof InputQuantity;
|
||||
LanguageDialog: typeof LanguageDialog;
|
||||
MarkdownEditor: typeof MarkdownEditor;
|
||||
RecipeJsonEditor: typeof RecipeJsonEditor;
|
||||
|
||||
@@ -11082,7 +11082,7 @@ vite-plugin-vue-tracer@^1.0.1:
|
||||
pathe "^2.0.3"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
vite-plugin-vuetify@^2.1.0:
|
||||
vite-plugin-vuetify@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.2.tgz#8e28dcb5b20f4635d350010547654cf2b4dad373"
|
||||
integrity sha512-I/wd6QS+DO6lHmuGoi1UTyvvBTQ2KDzQZ9oowJQEJ6OcjWfJnscYXx2ptm6S7fJSASuZT8jGRBL3LV4oS3LpaA==
|
||||
@@ -11224,10 +11224,10 @@ vue@^3.4, vue@^3.5.13, vue@^3.5.22:
|
||||
"@vue/server-renderer" "3.5.22"
|
||||
"@vue/shared" "3.5.22"
|
||||
|
||||
vuetify-nuxt-module@^0.18.8:
|
||||
version "0.18.8"
|
||||
resolved "https://registry.yarnpkg.com/vuetify-nuxt-module/-/vuetify-nuxt-module-0.18.8.tgz#04b7b04606cbf22cb9b8317309db6166a8058654"
|
||||
integrity sha512-57J0MgmRTyEX4ZIIXZUyY+UodDi+hoJ9/UnUZQoBbpTc/9WAZyRonaJedIcHLnTBKyIxxWdvtWHkJdosngX3NQ==
|
||||
vuetify-nuxt-module@^0.18.9:
|
||||
version "0.18.9"
|
||||
resolved "https://registry.yarnpkg.com/vuetify-nuxt-module/-/vuetify-nuxt-module-0.18.9.tgz#154b8f8e689da4fac3bcb2372ef0e745b8e0b536"
|
||||
integrity sha512-jr+Hujsw5U465LKJD1/SQgqJIXuJNbXM0HOp9vPmtPFPlGFwZ4kb1YfUPNmuCEDaSIY6rkb7+W+FEJvt9PQELQ==
|
||||
dependencies:
|
||||
"@nuxt/kit" "^3.12.4"
|
||||
defu "^6.1.4"
|
||||
@@ -11238,7 +11238,7 @@ vuetify-nuxt-module@^0.18.8:
|
||||
ufo "^1.5.4"
|
||||
unconfig "^0.5.5"
|
||||
upath "^2.0.1"
|
||||
vite-plugin-vuetify "^2.1.0"
|
||||
vite-plugin-vuetify "^2.1.2"
|
||||
vuetify "^3.7.0"
|
||||
|
||||
vuetify@^3.7.0:
|
||||
|
||||
Reference in New Issue
Block a user