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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user