mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 10:13:32 -04:00 
			
		
		
		
	ux: unify UI based on user-feedback (#1216)
* unify UI based on user-feedback * fix layout shify error * implement drag and drop animation
This commit is contained in:
		| @@ -24,19 +24,21 @@ | |||||||
|             </div> |             </div> | ||||||
|           </v-expand-transition> |           </v-expand-transition> | ||||||
|         </RecipeCardImage> |         </RecipeCardImage> | ||||||
|         <v-card-title class="my-n3 mb-n6"> |         <v-card-title class="my-n3 px-2 mb-n6"> | ||||||
|           <div class="headerClass"> |           <div class="headerClass"> | ||||||
|             {{ name }} |             {{ name }} | ||||||
|           </div> |           </div> | ||||||
|         </v-card-title> |         </v-card-title> | ||||||
|  |  | ||||||
|         <slot name="actions"> |         <slot name="actions"> | ||||||
|           <v-card-actions> |           <v-card-actions class="px-1"> | ||||||
|             <RecipeFavoriteBadge v-if="loggedIn" :slug="slug" show-always /> |             <RecipeFavoriteBadge v-if="loggedIn" class="absolute" :slug="slug" show-always /> | ||||||
|             <RecipeRating :value="rating" :name="name" :slug="slug" :small="true" /> |  | ||||||
|  |             <RecipeRating class="pb-1" :value="rating" :name="name" :slug="slug" :small="true" /> | ||||||
|             <v-spacer></v-spacer> |             <v-spacer></v-spacer> | ||||||
|             <RecipeChips :truncate="true" :items="tags" :title="false" :limit="2" :small="true" url-prefix="tags" /> |             <RecipeChips :truncate="true" :items="tags" :title="false" :limit="2" :small="true" url-prefix="tags" /> | ||||||
|             <RecipeContextMenu |             <RecipeContextMenu | ||||||
|  |               color="grey darken-2" | ||||||
|               :slug="slug" |               :slug="slug" | ||||||
|               :name="name" |               :name="name" | ||||||
|               :recipe-id="recipeId" |               :recipe-id="recipeId" | ||||||
|   | |||||||
| @@ -40,6 +40,7 @@ | |||||||
|  |  | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import { defineComponent, reactive, toRefs, useContext } from "@nuxtjs/composition-api"; | import { defineComponent, reactive, toRefs, useContext } from "@nuxtjs/composition-api"; | ||||||
|  | import colors from "vuetify/lib/util/colors"; | ||||||
| import { useUserApi } from "~/composables/api"; | import { useUserApi } from "~/composables/api"; | ||||||
|  |  | ||||||
| export interface ContextMenuIncludes { | export interface ContextMenuIncludes { | ||||||
| @@ -91,7 +92,7 @@ export default defineComponent({ | |||||||
|     }, |     }, | ||||||
|     color: { |     color: { | ||||||
|       type: String, |       type: String, | ||||||
|       default: "primary", |       default: colors.grey.darken2, | ||||||
|     }, |     }, | ||||||
|     slug: { |     slug: { | ||||||
|       type: String, |       type: String, | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ | |||||||
|       </template> |       </template> | ||||||
|  |  | ||||||
|       <v-card> |       <v-card> | ||||||
|         <v-app-bar dark color="primary" class="mt-n1 mb-3"> |         <v-app-bar dense dark color="primary" class="mb-2"> | ||||||
|           <v-icon large left> |           <v-icon large left> | ||||||
|             {{ $globals.icons.createAlt }} |             {{ $globals.icons.createAlt }} | ||||||
|           </v-icon> |           </v-icon> | ||||||
| @@ -21,34 +21,26 @@ | |||||||
|             v-model="inputText" |             v-model="inputText" | ||||||
|             outlined |             outlined | ||||||
|             rows="12" |             rows="12" | ||||||
|  |             hide-details | ||||||
|             :placeholder="$t('new-recipe.paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list')" |             :placeholder="$t('new-recipe.paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list')" | ||||||
|           > |           > | ||||||
|           </v-textarea> |           </v-textarea> | ||||||
|           <v-tooltip top> |  | ||||||
|             <template #activator="{ on, attrs }"> |  | ||||||
|               <v-btn outlined color="info" small v-bind="attrs" @click="trimAllLines" v-on="on"> |  | ||||||
|                 Trim Whitespace |  | ||||||
|               </v-btn> |  | ||||||
|             </template> |  | ||||||
|             <span> Trim leading and trailing whitespace as well as blank lines </span> |  | ||||||
|           </v-tooltip> |  | ||||||
|  |  | ||||||
|           <v-tooltip top> |           <v-divider></v-divider> | ||||||
|             <template #activator="{ on, attrs }"> |           <template v-for="(util, idx) in utilities"> | ||||||
|               <v-btn class="ml-1" outlined color="info" small v-bind="attrs" @click="removeFirstCharacter" v-on="on"> |             <v-list-item :key="util.id" dense class="py-1"> | ||||||
|                 Trim Prefix |               <v-list-item-title> | ||||||
|               </v-btn> |                 <v-list-item-subtitle class="wrap-word"> | ||||||
|             </template> |                   {{ util.description }} | ||||||
|             <span> Trim first character from each line </span> |                 </v-list-item-subtitle> | ||||||
|           </v-tooltip> |               </v-list-item-title> | ||||||
|           <v-tooltip top> |               <BaseButton small color="info" @click="util.action"> | ||||||
|             <template #activator="{ on, attrs }"> |                 <template #icon> {{ $globals.icons.robot }}</template> | ||||||
|               <v-btn class="ml-1" outlined color="info" small v-bind="attrs" @click="splitByNumberedLine" v-on="on"> |                 Run | ||||||
|                 Split By Numbered Line |               </BaseButton> | ||||||
|               </v-btn> |             </v-list-item> | ||||||
|             </template> |             <v-divider :key="`divider-${idx}`" class="mx-2"></v-divider> | ||||||
|             <span> Attempts to split a paragraph by matching 1) or 1. patterns </span> |           </template> | ||||||
|           </v-tooltip> |  | ||||||
|         </v-card-text> |         </v-card-text> | ||||||
|  |  | ||||||
|         <v-divider></v-divider> |         <v-divider></v-divider> | ||||||
| @@ -64,7 +56,7 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import { reactive, toRefs, defineComponent } from "@nuxtjs/composition-api"; | import { reactive, toRefs, defineComponent, useContext } from "@nuxtjs/composition-api"; | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
|   setup(_, context) { |   setup(_, context) { | ||||||
|     const state = reactive({ |     const state = reactive({ | ||||||
| @@ -78,7 +70,7 @@ export default defineComponent({ | |||||||
|  |  | ||||||
|     function removeFirstCharacter() { |     function removeFirstCharacter() { | ||||||
|       state.inputText = splitText() |       state.inputText = splitText() | ||||||
|         .map((line) => line.substr(1)) |         .map((line) => line.substring(1)) | ||||||
|         .join("\n"); |         .join("\n"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -109,7 +101,28 @@ export default defineComponent({ | |||||||
|       state.dialog = false; |       state.dialog = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const { i18n } = useContext(); | ||||||
|  |  | ||||||
|  |     const utilities = [ | ||||||
|  |       { | ||||||
|  |         id: "trim-whitespace", | ||||||
|  |         description: i18n.tc("new-recipe.trim-whitespace-description"), | ||||||
|  |         action: trimAllLines, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         id: "trim-prefix", | ||||||
|  |         description: i18n.tc("new-recipe.trim-prefix-description"), | ||||||
|  |         action: removeFirstCharacter, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         id: "split-by-numbered-line", | ||||||
|  |         description: i18n.tc("new-recipe.split-by-numbered-line-description"), | ||||||
|  |         action: splitByNumberedLine, | ||||||
|  |       }, | ||||||
|  |     ]; | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|  |       utilities, | ||||||
|       splitText, |       splitText, | ||||||
|       trimAllLines, |       trimAllLines, | ||||||
|       removeFirstCharacter, |       removeFirstCharacter, | ||||||
|   | |||||||
| @@ -21,18 +21,12 @@ | |||||||
|           type="number" |           type="number" | ||||||
|           placeholder="Quantity" |           placeholder="Quantity" | ||||||
|         > |         > | ||||||
|           <v-icon |           <v-icon v-if="$listeners && $listeners.delete" slot="prepend" class="mr-n1 handle"> | ||||||
|             v-if="$listeners && $listeners.delete" |             {{ $globals.icons.arrowUpDown }} | ||||||
|             slot="prepend" |  | ||||||
|             class="mr-n1" |  | ||||||
|             color="error" |  | ||||||
|             @click="$emit('delete')" |  | ||||||
|           > |  | ||||||
|             {{ $globals.icons.delete }} |  | ||||||
|           </v-icon> |           </v-icon> | ||||||
|         </v-text-field> |         </v-text-field> | ||||||
|       </v-col> |       </v-col> | ||||||
|       <v-col v-if="!disableAmount && units" sm="12" md="3" cols="12"> |       <v-col v-if="!disableAmount" sm="12" md="3" cols="12"> | ||||||
|         <v-autocomplete |         <v-autocomplete | ||||||
|           v-model="value.unit" |           v-model="value.unit" | ||||||
|           :search-input.sync="unitSearch" |           :search-input.sync="unitSearch" | ||||||
| @@ -40,7 +34,7 @@ | |||||||
|           dense |           dense | ||||||
|           solo |           solo | ||||||
|           return-object |           return-object | ||||||
|           :items="units" |           :items="units || []" | ||||||
|           item-text="name" |           item-text="name" | ||||||
|           class="mx-1" |           class="mx-1" | ||||||
|           placeholder="Choose Unit" |           placeholder="Choose Unit" | ||||||
| @@ -59,7 +53,7 @@ | |||||||
|       </v-col> |       </v-col> | ||||||
|  |  | ||||||
|       <!-- Foods Input --> |       <!-- Foods Input --> | ||||||
|       <v-col v-if="!disableAmount && foods" m="12" md="3" cols="12" class=""> |       <v-col v-if="!disableAmount" m="12" md="3" cols="12" class=""> | ||||||
|         <v-autocomplete |         <v-autocomplete | ||||||
|           v-model="value.food" |           v-model="value.food" | ||||||
|           :search-input.sync="foodSearch" |           :search-input.sync="foodSearch" | ||||||
| @@ -67,7 +61,7 @@ | |||||||
|           dense |           dense | ||||||
|           solo |           solo | ||||||
|           return-object |           return-object | ||||||
|           :items="foods" |           :items="foods || []" | ||||||
|           item-text="name" |           item-text="name" | ||||||
|           class="mx-1 py-0" |           class="mx-1 py-0" | ||||||
|           placeholder="Choose Food" |           placeholder="Choose Food" | ||||||
| @@ -85,28 +79,34 @@ | |||||||
|         </v-autocomplete> |         </v-autocomplete> | ||||||
|       </v-col> |       </v-col> | ||||||
|       <v-col sm="12" md="" cols="12"> |       <v-col sm="12" md="" cols="12"> | ||||||
|         <v-text-field v-model="value.note" hide-details dense solo class="mx-1" placeholder="Notes"> |         <div class="d-flex"> | ||||||
|           <v-icon v-if="disableAmount" slot="prepend" class="mr-n1" color="error" @click="$emit('delete')"> |           <v-text-field v-model="value.note" hide-details dense solo class="mx-1" placeholder="Notes"> | ||||||
|             {{ $globals.icons.delete }} |             <v-icon v-if="disableAmount && $listeners && $listeners.delete" slot="prepend" class="mr-n1 handle"> | ||||||
|           </v-icon> |               {{ $globals.icons.arrowUpDown }} | ||||||
|  |             </v-icon> | ||||||
|           <template slot="append-outer"> |           </v-text-field> | ||||||
|             <BaseButtonGroup |           <BaseButtonGroup | ||||||
|               :large="false" |             hover | ||||||
|               class="handle my-auto" |             :large="false" | ||||||
|               :buttons="[ |             class="my-auto" | ||||||
|                 { |             :buttons="[ | ||||||
|                   icon: $globals.icons.arrowUpDown, |               { | ||||||
|                   text: '', |                 icon: $globals.icons.delete, | ||||||
|                   event: 'open', |                 text: 'Delete', | ||||||
|                   children: contextMenuOptions, |                 event: 'delete', | ||||||
|                 }, |               }, | ||||||
|               ]" |               { | ||||||
|               @toggle-section="toggleTitle" |                 icon: $globals.icons.dotsVertical, | ||||||
|               @toggle-original="toggleOriginalText" |                 text: 'Menu', | ||||||
|             /> |                 event: 'open', | ||||||
|           </template> |                 children: contextMenuOptions, | ||||||
|         </v-text-field> |               }, | ||||||
|  |             ]" | ||||||
|  |             @toggle-section="toggleTitle" | ||||||
|  |             @toggle-original="toggleOriginalText" | ||||||
|  |             @delete="$emit('delete')" | ||||||
|  |           /> | ||||||
|  |         </div> | ||||||
|       </v-col> |       </v-col> | ||||||
|     </v-row> |     </v-row> | ||||||
|     <p v-if="showOriginalText" class="text-caption">Original Text: {{ value.originalText }}</p> |     <p v-if="showOriginalText" class="text-caption">Original Text: {{ value.originalText }}</p> | ||||||
|   | |||||||
| @@ -71,116 +71,126 @@ | |||||||
|       :disabled="!edit" |       :disabled="!edit" | ||||||
|       :value="value" |       :value="value" | ||||||
|       handle=".handle" |       handle=".handle" | ||||||
|  |       v-bind="{ | ||||||
|  |         animation: 200, | ||||||
|  |         group: 'description', | ||||||
|  |         ghostClass: 'ghost', | ||||||
|  |       }" | ||||||
|       @input="updateIndex" |       @input="updateIndex" | ||||||
|       @start="drag = true" |       @start="drag = true" | ||||||
|       @end="drag = false" |       @end="drag = false" | ||||||
|     > |     > | ||||||
|       <div v-for="(step, index) in value" :key="step.id"> |       <TransitionGroup type="transition" :name="!drag ? 'flip-list' : ''"> | ||||||
|         <v-app-bar |         <div v-for="(step, index) in value" :key="step.id" class="list-group-item"> | ||||||
|           v-if="showTitleEditor[step.id]" |           <v-app-bar | ||||||
|           class="primary mx-1 mt-6" |             v-if="showTitleEditor[step.id]" | ||||||
|           style="cursor: pointer" |             class="primary mx-1 mt-6" | ||||||
|           dark |             style="cursor: pointer" | ||||||
|           dense |             dark | ||||||
|           rounded |  | ||||||
|           @click="toggleCollapseSection(index)" |  | ||||||
|         > |  | ||||||
|           <v-toolbar-title v-if="!edit" class="headline"> |  | ||||||
|             <v-app-bar-title v-text="step.title"> </v-app-bar-title> |  | ||||||
|           </v-toolbar-title> |  | ||||||
|           <v-text-field |  | ||||||
|             v-if="edit" |  | ||||||
|             v-model="step.title" |  | ||||||
|             class="headline pa-0 mt-5" |  | ||||||
|             dense |             dense | ||||||
|             solo |             rounded | ||||||
|             flat |             @click="toggleCollapseSection(index)" | ||||||
|             :placeholder="$t('recipe.section-title')" |  | ||||||
|             background-color="primary" |  | ||||||
|           > |           > | ||||||
|           </v-text-field> |             <v-toolbar-title v-if="!edit" class="headline"> | ||||||
|         </v-app-bar> |               <v-app-bar-title v-text="step.title"> </v-app-bar-title> | ||||||
|         <v-hover v-slot="{ hover }"> |             </v-toolbar-title> | ||||||
|           <v-card |             <v-text-field | ||||||
|             class="ma-1" |               v-if="edit" | ||||||
|             :class="[{ 'on-hover': hover }, isChecked(index)]" |               v-model="step.title" | ||||||
|             :elevation="hover ? 12 : 2" |               class="headline pa-0 mt-5" | ||||||
|             :ripple="false" |               dense | ||||||
|             @click="toggleDisabled(index)" |               solo | ||||||
|           > |               flat | ||||||
|             <v-card-title :class="{ 'pb-0': !isChecked(index) }"> |               :placeholder="$t('recipe.section-title')" | ||||||
|               <v-btn v-if="edit" fab x-small color="white" class="mr-2" elevation="0" @click="value.splice(index, 1)"> |               background-color="primary" | ||||||
|                 <v-icon size="24" color="error">{{ $globals.icons.delete }}</v-icon> |             > | ||||||
|               </v-btn> |             </v-text-field> | ||||||
|  |           </v-app-bar> | ||||||
|               {{ $t("recipe.step-index", { step: index + 1 }) }} |           <v-hover v-slot="{ hover }"> | ||||||
|  |             <v-card | ||||||
|               <template v-if="edit"> |               class="ma-1" | ||||||
|                 <v-icon class="handle ml-auto mr-2">{{ $globals.icons.arrowUpDown }}</v-icon> |               :class="[{ 'on-hover': hover }, isChecked(index)]" | ||||||
|                 <div> |               :elevation="hover ? 12 : 2" | ||||||
|                   <BaseButtonGroup |               :ripple="false" | ||||||
|                     :buttons="[ |               @click="toggleDisabled(index)" | ||||||
|                       { |             > | ||||||
|                         icon: $globals.icons.dotsVertical, |               <v-card-title :class="{ 'pb-0': !isChecked(index) }"> | ||||||
|                         text: '', |                 <span class="handle"> | ||||||
|                         event: 'open', |                   <v-icon v-if="edit" size="26" class="pb-1">{{ $globals.icons.arrowUpDown }}</v-icon> | ||||||
|                         children: [ |                   {{ $t("recipe.step-index", { step: index + 1 }) }} | ||||||
|                           { |                 </span> | ||||||
|                             text: 'Toggle Section', |                 <template v-if="edit"> | ||||||
|                             event: 'toggle-section', |                   <div class="ml-auto"> | ||||||
|                           }, |                     <BaseButtonGroup | ||||||
|                           { |                       :large="false" | ||||||
|                             text: 'Link Ingredients', |                       :buttons="[ | ||||||
|                             event: 'link-ingredients', |                         { | ||||||
|                           }, |                           icon: $globals.icons.delete, | ||||||
|                           { |                           text: $tc('general.delete'), | ||||||
|                             text: 'Merge Above', |                           event: 'delete', | ||||||
|                             event: 'merge-above', |                         }, | ||||||
|                           }, |                         { | ||||||
|                         ], |                           icon: $globals.icons.dotsVertical, | ||||||
|                       }, |                           text: '', | ||||||
|                       { |                           event: 'open', | ||||||
|                         icon: previewStates[index] ? $globals.icons.edit : $globals.icons.eye, |                           children: [ | ||||||
|                         text: previewStates[index] ? $tc('general.edit') : 'Preview Markdown', |                             { | ||||||
|                         event: 'preview-step', |                               text: 'Toggle Section', | ||||||
|                       }, |                               event: 'toggle-section', | ||||||
|                     ]" |                             }, | ||||||
|                     @merge-above="mergeAbove(index - 1, index)" |                             { | ||||||
|                     @toggle-section="toggleShowTitle(step.id)" |                               text: 'Link Ingredients', | ||||||
|                     @link-ingredients="openDialog(index, step.ingredientReferences, step.text)" |                               event: 'link-ingredients', | ||||||
|                     @preview-step="togglePreviewState(index)" |                             }, | ||||||
|                   /> |                             { | ||||||
|  |                               text: 'Merge Above', | ||||||
|  |                               event: 'merge-above', | ||||||
|  |                             }, | ||||||
|  |                             { | ||||||
|  |                               icon: previewStates[index] ? $globals.icons.edit : $globals.icons.eye, | ||||||
|  |                               text: previewStates[index] ? 'Edit Markdown' : 'Preview Markdown', | ||||||
|  |                               event: 'preview-step', | ||||||
|  |                             }, | ||||||
|  |                           ], | ||||||
|  |                         }, | ||||||
|  |                       ]" | ||||||
|  |                       @merge-above="mergeAbove(index - 1, index)" | ||||||
|  |                       @toggle-section="toggleShowTitle(step.id)" | ||||||
|  |                       @link-ingredients="openDialog(index, step.ingredientReferences, step.text)" | ||||||
|  |                       @preview-step="togglePreviewState(index)" | ||||||
|  |                       @delete="value.splice(index, 1)" | ||||||
|  |                     /> | ||||||
|  |                   </div> | ||||||
|  |                 </template> | ||||||
|  |                 <v-fade-transition> | ||||||
|  |                   <v-icon v-show="isChecked(index)" size="24" class="ml-auto" color="success"> | ||||||
|  |                     {{ $globals.icons.checkboxMarkedCircle }} | ||||||
|  |                   </v-icon> | ||||||
|  |                 </v-fade-transition> | ||||||
|  |               </v-card-title> | ||||||
|  |               <v-card-text v-if="edit"> | ||||||
|  |                 <MarkdownEditor | ||||||
|  |                   v-model="value[index]['text']" | ||||||
|  |                   :preview.sync="previewStates[index]" | ||||||
|  |                   :display-preview="false" | ||||||
|  |                 /> | ||||||
|  |                 <div | ||||||
|  |                   v-for="ing in step.ingredientReferences" | ||||||
|  |                   :key="ing.referenceId" | ||||||
|  |                   v-html="getIngredientByRefId(ing.referenceId)" | ||||||
|  |                 /> | ||||||
|  |               </v-card-text> | ||||||
|  |               <v-expand-transition> | ||||||
|  |                 <div v-show="!isChecked(index) && !edit" class="m-0 p-0"> | ||||||
|  |                   <v-card-text class="markdown"> | ||||||
|  |                     <VueMarkdown class="markdown" :source="step.text"> </VueMarkdown> | ||||||
|  |                   </v-card-text> | ||||||
|                 </div> |                 </div> | ||||||
|               </template> |               </v-expand-transition> | ||||||
|  |             </v-card> | ||||||
|               <v-fade-transition> |           </v-hover> | ||||||
|                 <v-icon v-show="isChecked(index)" size="24" class="ml-auto" color="success"> |         </div> | ||||||
|                   {{ $globals.icons.checkboxMarkedCircle }} |       </TransitionGroup> | ||||||
|                 </v-icon> |  | ||||||
|               </v-fade-transition> |  | ||||||
|             </v-card-title> |  | ||||||
|             <v-card-text v-if="edit"> |  | ||||||
|               <MarkdownEditor |  | ||||||
|                 v-model="value[index]['text']" |  | ||||||
|                 :preview.sync="previewStates[index]" |  | ||||||
|                 :display-preview="false" |  | ||||||
|               /> |  | ||||||
|               <div |  | ||||||
|                 v-for="ing in step.ingredientReferences" |  | ||||||
|                 :key="ing.referenceId" |  | ||||||
|                 v-html="getIngredientByRefId(ing.referenceId)" |  | ||||||
|               /> |  | ||||||
|             </v-card-text> |  | ||||||
|             <v-expand-transition> |  | ||||||
|               <div v-show="!isChecked(index) && !edit" class="m-0 p-0"> |  | ||||||
|                 <v-card-text class="markdown"> |  | ||||||
|                   <VueMarkdown class="markdown" :source="step.text"> </VueMarkdown> |  | ||||||
|                 </v-card-text> |  | ||||||
|               </div> |  | ||||||
|             </v-expand-transition> |  | ||||||
|           </v-card> |  | ||||||
|         </v-hover> |  | ||||||
|       </div> |  | ||||||
|     </draggable> |     </draggable> | ||||||
|   </section> |   </section> | ||||||
| </template> | </template> | ||||||
| @@ -481,7 +491,10 @@ export default defineComponent({ | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const drag = ref(false); | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|  |       drag, | ||||||
|       togglePreviewState, |       togglePreviewState, | ||||||
|       toggleCollapseSection, |       toggleCollapseSection, | ||||||
|       previewStates, |       previewStates, | ||||||
| @@ -521,4 +534,23 @@ export default defineComponent({ | |||||||
| .markdown >>> ol > li { | .markdown >>> ol > li { | ||||||
|   display: list-item; |   display: list-item; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .flip-list-move { | ||||||
|  |   transition: transform 0.5s; | ||||||
|  | } | ||||||
|  | .no-move { | ||||||
|  |   transition: transform 0s; | ||||||
|  | } | ||||||
|  | .ghost { | ||||||
|  |   opacity: 0.5; | ||||||
|  | } | ||||||
|  | .list-group { | ||||||
|  |   min-height: 38px; | ||||||
|  | } | ||||||
|  | .list-group-item { | ||||||
|  |   cursor: move; | ||||||
|  | } | ||||||
|  | .list-group-item i { | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
| @@ -1,29 +1,27 @@ | |||||||
| <template> | <template> | ||||||
|   <div v-if="value.length > 0 || edit"> |   <div v-if="value.length > 0 || edit" class="mt-8"> | ||||||
|     <h2 class="my-4">{{ $t("recipe.note") }}</h2> |     <h2 class="my-4">{{ $t("recipe.note") }}</h2> | ||||||
|     <v-card v-for="(note, index) in value" :key="'note' + index" class="mt-1"> |     <div v-for="(note, index) in value" :key="'note' + index" class="mt-1"> | ||||||
|       <div v-if="edit"> |       <v-card v-if="edit"> | ||||||
|         <v-card-text> |         <v-card-text> | ||||||
|           <v-row align="center"> |           <div class="d-flex align-center"> | ||||||
|             <v-btn fab x-small color="white" class="mr-2" elevation="0" @click="removeByIndex(value, index)"> |             <v-text-field v-model="value[index]['title']" :label="$t('recipe.title')" /> | ||||||
|               <v-icon color="error">{{ $globals.icons.delete }}</v-icon> |             <v-btn icon class="mr-2" elevation="0" @click="removeByIndex(value, index)"> | ||||||
|  |               <v-icon>{{ $globals.icons.delete }}</v-icon> | ||||||
|             </v-btn> |             </v-btn> | ||||||
|             <v-text-field v-model="value[index]['title']" :label="$t('recipe.title')"></v-text-field> |           </div> | ||||||
|           </v-row> |           <v-textarea v-model="value[index]['text']" auto-grow :placeholder="$t('recipe.note')" /> | ||||||
|  |  | ||||||
|           <v-textarea v-model="value[index]['text']" auto-grow :placeholder="$t('recipe.note')"> </v-textarea> |  | ||||||
|         </v-card-text> |         </v-card-text> | ||||||
|       </div> |       </v-card> | ||||||
|       <div v-else> |       <div v-else> | ||||||
|         <v-card-title class="py-2"> |         <v-card-title class="text-subtitle-1 font-weight-medium py-1"> | ||||||
|           {{ note.title }} |           {{ note.title }} | ||||||
|         </v-card-title> |         </v-card-title> | ||||||
|         <v-divider class="mx-2"></v-divider> |  | ||||||
|         <v-card-text> |         <v-card-text> | ||||||
|           <VueMarkdown :source="note.text"> </VueMarkdown> |           <VueMarkdown :source="note.text"> </VueMarkdown> | ||||||
|         </v-card-text> |         </v-card-text> | ||||||
|       </div> |       </div> | ||||||
|     </v-card> |     </div> | ||||||
|  |  | ||||||
|     <div v-if="edit" class="d-flex justify-end"> |     <div v-if="edit" class="d-flex justify-end"> | ||||||
|       <BaseButton class="ml-auto my-2" @click="addNote"> {{ $t("general.new") }}</BaseButton> |       <BaseButton class="ml-auto my-2" @click="addNote"> {{ $t("general.new") }}</BaseButton> | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| <template> | <template> | ||||||
|   <v-item-group> |   <v-item-group> | ||||||
|     <template v-for="btn in buttons"> |     <template v-for="btn in buttons"> | ||||||
|       <v-menu v-if="btn.children" :key="'menu-' + btn.event" active-class="pa-0" offset-x left> |       <v-menu v-if="btn.children" :key="'menu-' + btn.event" active-class="pa-0" offset-y top left> | ||||||
|         <template #activator="{ on, attrs }"> |         <template #activator="{ on, attrs }"> | ||||||
|           <v-btn tile :large="large" icon v-bind="attrs" v-on="on"> |           <v-btn tile :large="large" icon v-bind="attrs" v-on="on"> | ||||||
|             <v-icon> |             <v-icon> | ||||||
|   | |||||||
| @@ -215,7 +215,10 @@ | |||||||
|     "upload-a-recipe": "Upload a Recipe", |     "upload-a-recipe": "Upload a Recipe", | ||||||
|     "upload-individual-zip-file": "Upload an individual .zip file exported from another Mealie instance.", |     "upload-individual-zip-file": "Upload an individual .zip file exported from another Mealie instance.", | ||||||
|     "url-form-hint": "Copy and paste a link from your favorite recipe website", |     "url-form-hint": "Copy and paste a link from your favorite recipe website", | ||||||
|     "view-scraped-data": "View Scraped Data" |     "view-scraped-data": "View Scraped Data", | ||||||
|  |     "trim-whitespace-description": "Trim leading and trailing whitespace as well as blank lines", | ||||||
|  |     "trim-prefix-description": "Trim first character from each line", | ||||||
|  |     "split-by-numbered-line-description": "Attempts to split a paragraph by matching '1)' or '1.' patterns" | ||||||
|   }, |   }, | ||||||
|   "page": { |   "page": { | ||||||
|     "404-page-not-found": "404 Page not found", |     "404-page-not-found": "404 Page not found", | ||||||
|   | |||||||
| @@ -127,14 +127,29 @@ | |||||||
|           <!-- Advanced Editor --> |           <!-- Advanced Editor --> | ||||||
|           <div v-if="form"> |           <div v-if="form"> | ||||||
|             <h2 class="mb-4">{{ $t("recipe.ingredients") }}</h2> |             <h2 class="mb-4">{{ $t("recipe.ingredients") }}</h2> | ||||||
|             <draggable v-if="recipe.recipeIngredient.length > 0" v-model="recipe.recipeIngredient" handle=".handle"> |             <draggable | ||||||
|               <RecipeIngredientEditor |               v-if="recipe.recipeIngredient.length > 0" | ||||||
|                 v-for="(ingredient, index) in recipe.recipeIngredient" |               v-model="recipe.recipeIngredient" | ||||||
|                 :key="ingredient.referenceId" |               handle=".handle" | ||||||
|                 v-model="recipe.recipeIngredient[index]" |               v-bind="{ | ||||||
|                 :disable-amount="recipe.settings.disableAmount" |                 animation: 200, | ||||||
|                 @delete="recipe.recipeIngredient.splice(index, 1)" |                 group: 'description', | ||||||
|               /> |                 disabled: false, | ||||||
|  |                 ghostClass: 'ghost', | ||||||
|  |               }" | ||||||
|  |               @start="drag = true" | ||||||
|  |               @end="drag = false" | ||||||
|  |             > | ||||||
|  |               <TransitionGroup type="transition" :name="!drag ? 'flip-list' : ''"> | ||||||
|  |                 <RecipeIngredientEditor | ||||||
|  |                   v-for="(ingredient, index) in recipe.recipeIngredient" | ||||||
|  |                   :key="ingredient.referenceId" | ||||||
|  |                   v-model="recipe.recipeIngredient[index]" | ||||||
|  |                   class="list-group-item" | ||||||
|  |                   :disable-amount="recipe.settings.disableAmount" | ||||||
|  |                   @delete="recipe.recipeIngredient.splice(index, 1)" | ||||||
|  |                 /> | ||||||
|  |               </TransitionGroup> | ||||||
|             </draggable> |             </draggable> | ||||||
|             <v-skeleton-loader v-else boilerplate elevation="2" type="list-item"> </v-skeleton-loader> |             <v-skeleton-loader v-else boilerplate elevation="2" type="list-item"> </v-skeleton-loader> | ||||||
|             <div class="d-flex justify-end mt-2"> |             <div class="d-flex justify-end mt-2"> | ||||||
| @@ -355,7 +370,7 @@ | |||||||
|                       :tag-selector="true" |                       :tag-selector="true" | ||||||
|                       :show-label="false" |                       :show-label="false" | ||||||
|                     /> |                     /> | ||||||
|                     <RecipeChips v-else :items="recipe.tags" url-prefix="tags"/> |                     <RecipeChips v-else :items="recipe.tags" url-prefix="tags" /> | ||||||
|                   </v-card-text> |                   </v-card-text> | ||||||
|                 </v-card> |                 </v-card> | ||||||
|  |  | ||||||
| @@ -820,8 +835,11 @@ export default defineComponent({ | |||||||
|       return "Parse ingredients"; |       return "Parse ingredients"; | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     const drag = ref(false); | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|       // Wake Lock |       // Wake Lock | ||||||
|  |       drag, | ||||||
|       wakeIsSupported, |       wakeIsSupported, | ||||||
|       isActive, |       isActive, | ||||||
|       lockScreen, |       lockScreen, | ||||||
| @@ -857,3 +875,24 @@ export default defineComponent({ | |||||||
|   head: {}, |   head: {}, | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | <style lang="css"> | ||||||
|  | .flip-list-move { | ||||||
|  |   transition: transform 0.5s; | ||||||
|  | } | ||||||
|  | .no-move { | ||||||
|  |   transition: transform 0s; | ||||||
|  | } | ||||||
|  | .ghost { | ||||||
|  |   opacity: 0.5; | ||||||
|  | } | ||||||
|  | .list-group { | ||||||
|  |   min-height: 38px; | ||||||
|  | } | ||||||
|  | .list-group-item { | ||||||
|  |   cursor: move; | ||||||
|  | } | ||||||
|  | .list-group-item i { | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user