1
0
mirror of https://github.com/TandoorRecipes/recipes.git synced 2026-01-11 09:07:12 -05:00

many editor improvements (and more)

This commit is contained in:
vabene1111
2025-01-04 17:59:56 +01:00
parent c691d6028b
commit c75d265686
40 changed files with 256 additions and 61 deletions

View File

@@ -1,11 +1,8 @@
<template>
<!-- TODO label is not showing for some reason, for now in placeholder -->
<!-- TODO support density prop -->
<v-input :hint="props.hint" persistent-hint :label="props.label">
<template #prepend>
<slot name="prepend">
</slot>
<slot name="prepend"></slot>
</template>
<!-- TODO resolve-on-load false for now, race condition with model class, make prop once better solution is found -->
<Multiselect
@@ -160,7 +157,8 @@ async function createObject(object: any, select$: Multiselect) {
</script>
<style src="@vueform/multiselect/themes/default.css"></style>
<style scoped>
<!-- style can't be scoped (for whatever reason) -->
<style>
.material-multiselect {
--ms-bg: rgba(210, 210, 210, 0.1);
--ms-border-color: 0;

View File

@@ -5,7 +5,7 @@
<v-card variant="outlined">
<template #title>
<v-card-title>
<v-chip color="primary">{{ props.stepIndex + 1 }}</v-chip>
<v-chip color="primary">{{$t('Step')}} {{ props.stepIndex + 1 }}</v-chip>
{{ step.name }}
</v-card-title>
</template>
@@ -54,18 +54,21 @@
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" v-if="!mobile">
<v-row v-for="(ingredient, index) in step.ingredients" dense>
<v-col cols="2">
<v-number-input :id="`id_input_amount_${step.id}_${index}`" :label="$t('Amount')" v-model="ingredient.amount" inset control-variant="stacked"
hide-details
:min="0"></v-number-input>
<v-text-field :id="`id_input_amount_${step.id}_${index}`" :label="$t('Amount')" type="number" v-model="ingredient.amount" density="compact" hide-details>
<template #prepend>
<v-icon icon="$dragHandle" class="drag-handle cursor-grab"></v-icon>
</template>
</v-text-field>
</v-col>
<v-col cols="3">
<model-select model="Unit" v-model="ingredient.unit" allow-create hide-details></model-select>
<model-select model="Unit" v-model="ingredient.unit" density="compact" allow-create hide-details></model-select>
</v-col>
<v-col cols="3">
<model-select model="Food" v-model="ingredient.food" allow-create hide-details></model-select>
<model-select model="Food" v-model="ingredient.food" density="compact" allow-create hide-details></model-select>
</v-col>
<v-col cols="3" @keydown.tab="event => handleIngredientNoteTab(event, index)">
<v-text-field :label="$t('Note')" v-model="ingredient.note" hide-details></v-text-field>
<v-text-field :label="$t('Note')" v-model="ingredient.note" density="compact" hide-details></v-text-field>
</v-col>
<v-col cols="1">
<v-btn variant="plain" icon>
@@ -76,7 +79,7 @@
</v-list>
</v-menu>
</v-btn>
<v-icon icon="$dragHandle" class="drag-handle"></v-icon>
</v-col>
</v-row>
</vue-draggable>
@@ -152,10 +155,21 @@
<v-card-text>
<v-form>
<v-number-input v-model="step.ingredients[editingIngredientIndex].amount" inset control-variant="stacked" autofocus :label="$t('Amount')"
:min="0"></v-number-input>
<model-select model="Unit" v-model="step.ingredients[editingIngredientIndex].unit" :label="$t('Unit')" allow-create></model-select>
<model-select model="Food" v-model="step.ingredients[editingIngredientIndex].food" :label="$t('Food')" allow-create></model-select>
<v-text-field :label="$t('Note')" v-model="step.ingredients[editingIngredientIndex].note"></v-text-field>
:min="0" v-if="!step.ingredients[editingIngredientIndex].isHeader"></v-number-input>
<model-select model="Unit" v-model="step.ingredients[editingIngredientIndex].unit" :label="$t('Unit')" v-if="!step.ingredients[editingIngredientIndex].isHeader"
allow-create></model-select>
<model-select model="Food" v-model="step.ingredients[editingIngredientIndex].food" :label="$t('Food')" v-if="!step.ingredients[editingIngredientIndex].isHeader"
allow-create></model-select>
<v-text-field :label="(step.ingredients[editingIngredientIndex].isHeader) ?$t('Headline') : $t('Note')"
v-model="step.ingredients[editingIngredientIndex].note"></v-text-field>
<v-checkbox
v-model="step.ingredients[editingIngredientIndex].isHeader"
:label="$t('Headline')"
:hint="$t('HeaderWarning')"
persistent-hint
@update:modelValue="step.ingredients[editingIngredientIndex].unit = null; step.ingredients[editingIngredientIndex].food = null; step.ingredients[editingIngredientIndex].amount = 0"
></v-checkbox>
</v-form>
</v-card-text>
<v-card-actions>
@@ -169,8 +183,8 @@
</template>
<script setup lang="ts">
import {nextTick, ref, useTemplateRef} from 'vue'
import {ApiApi, Ingredient, ParsedIngredient, Step} from "@/openapi";
import {nextTick, onMounted, ref} from 'vue'
import {ApiApi, Ingredient, ParsedIngredient, Step, Unit} from "@/openapi";
import StepMarkdownEditor from "@/components/inputs/StepMarkdownEditor.vue";
import {VNumberInput} from 'vuetify/labs/VNumberInput'
import ModelSelect from "@/components/inputs/ModelSelect.vue";
@@ -178,6 +192,8 @@ import {useDisplay} from "vuetify";
import {VueDraggable} from "vue-draggable-plus";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import IngredientString from "@/components/display/IngredientString.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
const emit = defineEmits(['delete'])
@@ -199,7 +215,24 @@ const dialogIngredientParser = ref(false)
const editingIngredientIndex = ref(Number)
const ingredientTextInput = ref("")
const ingredientDialogAmountRef = useTemplateRef('ref_input_amount_dialog')
const defaultUnit = ref<null | Unit>(null)
onMounted(() => {
let api = new ApiApi()
if (useUserPreferenceStore().userSettings.defaultUnit) {
api.apiUnitList({query: useUserPreferenceStore().userSettings.defaultUnit}).then(r => {
r.results.forEach(u => {
if (u.name == useUserPreferenceStore().userSettings.defaultUnit) {
defaultUnit.value = u
}
})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
})
}
})
/**
* sort function called by draggable when ingredient table is sorted
@@ -252,7 +285,13 @@ function handleIngredientNoteTab(event: KeyboardEvent, index: number) {
* insert a new ingredient and focus its first input
*/
function insertAndFocusIngredient() {
step.value.ingredients.push({} as Ingredient)
let ingredient = {} as Ingredient
if (defaultUnit.value != null) {
ingredient.unit = defaultUnit.value
}
step.value.ingredients.push(ingredient)
nextTick(() => {
if (mobile.value) {
editingIngredientIndex.value = step.value.ingredients.length - 1

View File

@@ -1,27 +1,62 @@
<template>
<mavon-editor v-model="steep.instruction" :autofocus="false"
style="z-index: auto" :id="'id_instruction_' + steep.id"
<mavon-editor v-model="step.instruction" :autofocus="false"
style="z-index: auto" :id="'id_instruction_' + step.id"
:language="'en'"
:toolbars="md_editor_toolbars" :defaultOpen="'edit'">
<template #left-toolbar-after>
<span class="op-icon-divider"></span>
<button
type="button"
@click="steep.instruction+= ' {{ scale(100) }}'"
@click="step.instruction+= ' {{ scale(100) }}'"
class="op-icon fas fa-calculator"
aria-hidden="true"
:title="$t('ScalableNumber')"
></button>
<button class="op-icon fa-solid fa-code">
<v-menu activator="parent">
<v-list density="compact">
<v-list-item
v-for="template in templates"
@click="step.instruction+= template.template"
>
<ingredient-string :ingredient="template.ingredient"></ingredient-string>
</v-list-item>
</v-list>
</v-menu>
</button>
</template>
</mavon-editor>
</template>
<script setup lang="ts">
import {Step} from "@/openapi";
import {Ingredient, Step} from "@/openapi";
import 'mavon-editor/dist/css/index.css'
import IngredientString from "@/components/display/IngredientString.vue";
import {computed} from "vue";
const steep = defineModel<Step>({required: true})
const step = defineModel<Step>({required: true})
type IngredientTemplate = {
name: string,
ingredient: Ingredient,
template: string,
}
const templates = computed(() => {
let templateList: IngredientTemplate[] = []
step.value.ingredients.forEach((ingredient, index) => {
if (!ingredient.isHeader && ingredient.food != null)
templateList.push({
name: ingredient.food.name,
ingredient: ingredient,
template: `{{ ingredients[${index}] }}{# ${ingredient.food.name} #}`
} as IngredientTemplate)
})
return templateList
})
const md_editor_toolbars = {
bold: true,