mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 10:13:32 -04:00 
			
		
		
		
	feat: Added a dedicated cookmode dialog that allows for individual scrolling (#4464)
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							65c35adc9d
						
					
				
				
					commit
					d419acd61e
				
			| @@ -1,75 +1,135 @@ | ||||
| <template> | ||||
|   <v-container :class="{ 'pa-0': $vuetify.breakpoint.smAndDown }"> | ||||
|     <v-card :flat="$vuetify.breakpoint.smAndDown" class="d-print-none"> | ||||
|       <RecipePageHeader | ||||
|   <div> | ||||
|     <v-container v-show="!isCookMode" key="recipe-page" :class="{ 'pa-0': $vuetify.breakpoint.smAndDown }"> | ||||
|       <v-card  :flat="$vuetify.breakpoint.smAndDown" class="d-print-none"> | ||||
|         <RecipePageHeader | ||||
|           :recipe="recipe" | ||||
|           :recipe-scale="scale" | ||||
|           :landscape="landscape" | ||||
|           @save="saveRecipe" | ||||
|           @delete="deleteRecipe" | ||||
|         /> | ||||
|         <LazyRecipeJsonEditor v-if="isEditJSON" v-model="recipe" class="mt-10" :options="EDITOR_OPTIONS" /> | ||||
|         <v-card-text v-else> | ||||
|           <!-- | ||||
|             This is where most of the main content is rendered. Some components include state for both Edit and View modes | ||||
|             which is why some have explicit v-if statements and others use the composition API to determine and manage | ||||
|             the shared state internally. | ||||
|  | ||||
|             The global recipe object is shared down the tree of components and _is_ mutated by child components. This is | ||||
|             some-what of a hack of the system and goes against the principles of Vue, but it _does_ seem to work and streamline | ||||
|             a significant amount of prop management. When we move to Vue 3 and have access to some of the newer API's the plan to update this | ||||
|             data management and mutation system we're using. | ||||
|           --> | ||||
|           <RecipePageEditorToolbar v-if="isEditForm" :recipe="recipe" /> | ||||
|           <RecipePageTitleContent :recipe="recipe" :landscape="landscape" /> | ||||
|           <RecipePageIngredientEditor v-if="isEditForm" :recipe="recipe" /> | ||||
|           <RecipePageScale :recipe="recipe" :scale.sync="scale" :landscape="landscape" /> | ||||
|  | ||||
|           <!-- | ||||
|             This section contains the 2 column layout for the recipe steps and other content. | ||||
|           --> | ||||
|           <v-row> | ||||
|             <!-- | ||||
|               The left column is conditionally rendered based on cook mode. | ||||
|             --> | ||||
|             <v-col v-if="!isCookMode || isEditForm" cols="12" sm="12" md="4" lg="4"> | ||||
|               <RecipePageIngredientToolsView v-if="!isEditForm" :recipe="recipe" :scale="scale" /> | ||||
|               <RecipePageOrganizers v-if="$vuetify.breakpoint.mdAndUp" :recipe="recipe" /> | ||||
|             </v-col> | ||||
|             <v-divider v-if="$vuetify.breakpoint.mdAndUp && !isCookMode" class="my-divider" :vertical="true" /> | ||||
|  | ||||
|             <!-- | ||||
|               the right column is always rendered, but it's layout width is determined by where the left column is | ||||
|               rendered. | ||||
|             --> | ||||
|             <v-col cols="12" sm="12" :md="8 + (isCookMode ? 1 : 0) * 4" :lg="8 + (isCookMode ? 1 : 0) * 4"> | ||||
|               <RecipePageInstructions | ||||
|                 v-model="recipe.recipeInstructions" | ||||
|                 :assets.sync="recipe.assets" | ||||
|                 :recipe="recipe" | ||||
|                 :scale="scale" | ||||
|               /> | ||||
|               <div v-if="isEditForm" class="d-flex"> | ||||
|                 <RecipeDialogBulkAdd class="ml-auto my-2 mr-1" @bulk-data="addStep" /> | ||||
|                 <BaseButton class="my-2" @click="addStep()"> {{ $t("general.add") }}</BaseButton> | ||||
|               </div> | ||||
|               <div v-if="!$vuetify.breakpoint.mdAndUp"> | ||||
|                 <RecipePageOrganizers :recipe="recipe" /> | ||||
|               </div> | ||||
|               <RecipeNotes v-model="recipe.notes" :edit="isEditForm" /> | ||||
|             </v-col> | ||||
|           </v-row> | ||||
|           <RecipePageFooter :recipe="recipe" /> | ||||
|         </v-card-text> | ||||
|       </v-card> | ||||
|       <WakelockSwitch/> | ||||
|       <RecipePageComments | ||||
|         v-if="!recipe.settings.disableComments && !isEditForm && !isCookMode" | ||||
|         :recipe="recipe" | ||||
|         :recipe-scale="scale" | ||||
|         :landscape="landscape" | ||||
|         @save="saveRecipe" | ||||
|         @delete="deleteRecipe" | ||||
|         class="px-1 my-4 d-print-none" | ||||
|       /> | ||||
|       <LazyRecipeJsonEditor v-if="isEditJSON" v-model="recipe" class="mt-10" :options="EDITOR_OPTIONS" /> | ||||
|       <v-card-text v-else> | ||||
|         <!-- | ||||
|           This is where most of the main content is rendered. Some components include state for both Edit and View modes | ||||
|           which is why some have explicit v-if statements and others use the composition API to determine and manage | ||||
|           the shared state internally. | ||||
|       <RecipePrintContainer :recipe="recipe" :scale="scale" /> | ||||
|     </v-container> | ||||
|     <!-- Cook mode displayes two columns with ingredients and instructions side by side, each being scrolled individually, allowing to view both at the same timer --> | ||||
|     <v-sheet v-show="isCookMode && !hasLinkedIngredients" key="cookmode" :style="{height: $vuetify.breakpoint.smAndUp ? 'calc(100vh - 48px)' : ''}"> <!-- the calc is to account for the toolbar a more dynamic solution could be needed  --> | ||||
|       <v-row  style="height: 100%;"  no-gutters class="overflow-hidden"> | ||||
|         <v-col  cols="12" sm="5" class="overflow-y-auto pl-4 pr-3 py-2" style="height: 100%;"> | ||||
|           <div class="d-flex align-center"> | ||||
|             <RecipePageScale :recipe="recipe" :scale.sync="scale" :landscape="landscape" /> | ||||
|           </div> | ||||
|           <RecipePageIngredientToolsView v-if="!isEditForm" :recipe="recipe" :scale="scale" :is-cook-mode="isCookMode" /> | ||||
|           <v-divider></v-divider> | ||||
|         </v-col> | ||||
|         <v-col class="overflow-y-auto py-2" style="height: 100%;" cols="12" sm="7"> | ||||
|           <RecipePageInstructions | ||||
|             v-model="recipe.recipeInstructions" | ||||
|             class="overflow-y-hidden px-4" | ||||
|             :assets.sync="recipe.assets" | ||||
|             :recipe="recipe" | ||||
|             :scale="scale" | ||||
|           /> | ||||
|         </v-col> | ||||
|       </v-row> | ||||
|  | ||||
|           The global recipe object is shared down the tree of components and _is_ mutated by child components. This is | ||||
|           some-what of a hack of the system and goes against the principles of Vue, but it _does_ seem to work and streamline | ||||
|           a significant amount of prop management. When we move to Vue 3 and have access to some of the newer API's the plan to update this | ||||
|           data management and mutation system we're using. | ||||
|          --> | ||||
|         <RecipePageEditorToolbar v-if="isEditForm" :recipe="recipe" /> | ||||
|         <RecipePageTitleContent :recipe="recipe" :landscape="landscape" /> | ||||
|         <RecipePageIngredientEditor v-if="isEditForm" :recipe="recipe" /> | ||||
|         <RecipePageScale :recipe="recipe" :scale.sync="scale" :landscape="landscape" /> | ||||
|  | ||||
|         <!-- | ||||
|           This section contains the 2 column layout for the recipe steps and other content. | ||||
|          --> | ||||
|         <v-row> | ||||
|           <!-- | ||||
|             The left column is conditionally rendered based on cook mode. | ||||
|            --> | ||||
|           <v-col v-if="!isCookMode || isEditForm" cols="12" sm="12" md="4" lg="4"> | ||||
|             <RecipePageIngredientToolsView v-if="!isEditForm" :recipe="recipe" :scale="scale" /> | ||||
|             <RecipePageOrganizers v-if="$vuetify.breakpoint.mdAndUp" :recipe="recipe" /> | ||||
|           </v-col> | ||||
|           <v-divider v-if="$vuetify.breakpoint.mdAndUp && !isCookMode" class="my-divider" :vertical="true" /> | ||||
|  | ||||
|           <!-- | ||||
|             the right column is always rendered, but it's layout width is determined by where the left column is | ||||
|             rendered. | ||||
|            --> | ||||
|           <v-col cols="12" sm="12" :md="8 + (isCookMode ? 1 : 0) * 4" :lg="8 + (isCookMode ? 1 : 0) * 4"> | ||||
|             <RecipePageInstructions | ||||
|               v-model="recipe.recipeInstructions" | ||||
|               :assets.sync="recipe.assets" | ||||
|               :recipe="recipe" | ||||
|     </v-sheet> | ||||
|     <v-sheet v-show="isCookMode && hasLinkedIngredients"> | ||||
|       <div class="mt-2 px-2 px-md-4"> | ||||
|         <RecipePageScale :recipe="recipe" :scale.sync="scale" :landscape="landscape"/> | ||||
|       </div> | ||||
|       <RecipePageInstructions | ||||
|         v-model="recipe.recipeInstructions" | ||||
|         class="overflow-y-hidden mt-n5 px-2 px-md-4" | ||||
|         :assets.sync="recipe.assets" | ||||
|         :recipe="recipe" | ||||
|         :scale="scale" | ||||
|       /> | ||||
|       <v-divider></v-divider> | ||||
|       <div class="px-2 px-md-4 pb-4 "> | ||||
|         <v-card flat> | ||||
|           <v-card-title>{{ $t('recipe.not-linked-ingredients') }}</v-card-title> | ||||
|             <RecipeIngredients | ||||
|               :value="notLinkedIngredients" | ||||
|               :scale="scale" | ||||
|             /> | ||||
|             <div v-if="isEditForm" class="d-flex"> | ||||
|               <RecipeDialogBulkAdd class="ml-auto my-2 mr-1" @bulk-data="addStep" /> | ||||
|               <BaseButton class="my-2" @click="addStep()"> {{ $t("general.add") }}</BaseButton> | ||||
|             </div> | ||||
|             <div v-if="!$vuetify.breakpoint.mdAndUp"> | ||||
|               <RecipePageOrganizers :recipe="recipe" /> | ||||
|             </div> | ||||
|             <RecipeNotes v-model="recipe.notes" :edit="isEditForm" /> | ||||
|           </v-col> | ||||
|         </v-row> | ||||
|         <RecipePageFooter :recipe="recipe" /> | ||||
|       </v-card-text> | ||||
|     </v-card> | ||||
|     <WakelockSwitch/> | ||||
|     <RecipePageComments | ||||
|       v-if="!recipe.settings.disableComments && !isEditForm && !isCookMode" | ||||
|       :recipe="recipe" | ||||
|       class="px-1 my-4 d-print-none" | ||||
|     /> | ||||
|     <RecipePrintContainer :recipe="recipe" :scale="scale" /> | ||||
|   </v-container> | ||||
|               :disable-amount="recipe.settings.disableAmount" | ||||
|               :is-cook-mode="isCookMode"> | ||||
|  | ||||
|             </RecipeIngredients> | ||||
|         </v-card> | ||||
|       </div> | ||||
|     </v-sheet> | ||||
|     <v-btn | ||||
|       v-if="isCookMode" | ||||
|       fab | ||||
|       small | ||||
|       color="primary" | ||||
|       style="position: fixed; right: 12px; top: 60px;" | ||||
|       @click="toggleCookMode()" | ||||
|       > | ||||
|       <v-icon>mdi-close</v-icon> | ||||
|     </v-btn> | ||||
|   </div> | ||||
|  | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| @@ -84,6 +144,7 @@ import { | ||||
| useRoute, | ||||
| } from "@nuxtjs/composition-api"; | ||||
| import { invoke, until } from "@vueuse/core"; | ||||
| import RecipeIngredients from "../RecipeIngredients.vue"; | ||||
| import RecipePageEditorToolbar from "./RecipePageParts/RecipePageEditorToolbar.vue"; | ||||
| import RecipePageFooter from "./RecipePageParts/RecipePageFooter.vue"; | ||||
| import RecipePageHeader from "./RecipePageParts/RecipePageHeader.vue"; | ||||
| @@ -133,6 +194,7 @@ export default defineComponent({ | ||||
|     RecipeNotes, | ||||
|     RecipePageInstructions, | ||||
|     RecipePageFooter, | ||||
|     RecipeIngredients | ||||
|   }, | ||||
|   props: { | ||||
|     recipe: { | ||||
| @@ -151,6 +213,13 @@ export default defineComponent({ | ||||
|     const { pageMode, editMode, setMode, isEditForm, isEditJSON, isCookMode, isEditMode, toggleCookMode } = | ||||
|       usePageState(props.recipe.slug); | ||||
|     const { deactivateNavigationWarning } = useNavigationWarning(); | ||||
|     const notLinkedIngredients = computed(() => { | ||||
|       console.log("inst",props.recipe.recipeInstruction); | ||||
|       return props.recipe.recipeIngredient.filter((ingredient) => { | ||||
|         return !props.recipe.recipeInstructions.some((step) => step.ingredientReferences?.map((ref) => ref.referenceId).includes(ingredient.referenceId)); | ||||
|       }) | ||||
|     }) | ||||
|     console.log(notLinkedIngredients); | ||||
|  | ||||
|     /** ============================================================= | ||||
|      * Recipe Snapshot on Mount | ||||
| @@ -176,11 +245,14 @@ export default defineComponent({ | ||||
|         } | ||||
|       } | ||||
|       deactivateNavigationWarning(); | ||||
|       toggleCookMode() | ||||
|  | ||||
|       clearPageState(props.recipe.slug || ""); | ||||
|       console.debug("reset RecipePage state during unmount"); | ||||
|     }); | ||||
|  | ||||
|     const hasLinkedIngredients = computed(() => { | ||||
|       return props.recipe.recipeInstructions.some((step) => step.ingredientReferences && step.ingredientReferences.length > 0); | ||||
|     }) | ||||
|     /** ============================================================= | ||||
|      * Set State onMounted | ||||
|      */ | ||||
| @@ -278,6 +350,8 @@ export default defineComponent({ | ||||
|       saveRecipe, | ||||
|       deleteRecipe, | ||||
|       addStep, | ||||
|       hasLinkedIngredients, | ||||
|       notLinkedIngredients | ||||
|     }; | ||||
|   }, | ||||
|   head: {}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user