mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	feat: Migrate to Nuxt 3 framework (#5184)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
		| @@ -1,9 +1,19 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <v-card-actions> | ||||
|       <v-menu v-if="tableConfig.hideColumns" offset-y bottom nudge-bottom="6" :close-on-content-click="false"> | ||||
|         <template #activator="{ on, attrs }"> | ||||
|           <v-btn color="accent" class="mr-2" dark v-bind="attrs" v-on="on"> | ||||
|       <v-menu | ||||
|         v-if="tableConfig.hideColumns" | ||||
|         offset-y | ||||
|         bottom | ||||
|         nudge-bottom="6" | ||||
|         :close-on-content-click="false" | ||||
|       > | ||||
|         <template #activator="{ props }"> | ||||
|           <v-btn | ||||
|             color="accent" | ||||
|             variant="elevated" | ||||
|             v-bind="props" | ||||
|           > | ||||
|             <v-icon> | ||||
|               {{ $globals.icons.cog }} | ||||
|             </v-icon> | ||||
| @@ -16,12 +26,12 @@ | ||||
|               :key="itemValue.text + itemValue.show" | ||||
|               v-model="filteredHeaders" | ||||
|               :value="itemValue.value" | ||||
|               dense | ||||
|               density="compact" | ||||
|               flat | ||||
|               inset | ||||
|               :label="itemValue.text" | ||||
|               hide-details | ||||
|             ></v-checkbox> | ||||
|             /> | ||||
|           </v-card-text> | ||||
|         </v-card> | ||||
|       </v-menu> | ||||
| @@ -30,41 +40,52 @@ | ||||
|         :disabled="selected.length < 1" | ||||
|         mode="event" | ||||
|         color="info" | ||||
|         variant="elevated" | ||||
|         :items="bulkActions" | ||||
|         v-on="bulkActionListener" | ||||
|       > | ||||
|       </BaseOverflowButton> | ||||
|       <slot name="button-row"> </slot> | ||||
|         v-bind="bulkActionListener" | ||||
|       /> | ||||
|       <slot name="button-row" /> | ||||
|     </v-card-actions> | ||||
|     <div class="mx-2 clip-width"> | ||||
|       <v-text-field v-model="search" :label="$t('search.search')"></v-text-field> | ||||
|       <v-text-field | ||||
|         v-model="search" | ||||
|         variant="underlined" | ||||
|         :label="$t('search.search')" | ||||
|       /> | ||||
|     </div> | ||||
|     <v-data-table | ||||
|       v-model="selected" | ||||
|       item-key="id" | ||||
|       :show-select="bulkActions.length > 0" | ||||
|       :headers="activeHeaders" | ||||
|       :sort-by="initialSort" | ||||
|       :sort-desc="initialSortDesc" | ||||
|       :show-select="bulkActions.length > 0" | ||||
|       :sort-by="sortBy" | ||||
|       :items="data || []" | ||||
|       :items-per-page="15" | ||||
|       :search="search" | ||||
|       class="elevation-2" | ||||
|     > | ||||
|       <template v-for="header in activeHeaders" #[`item.${header.value}`]="{ item }"> | ||||
|         <slot :name="'item.' + header.value" v-bind="{ item }"> {{ item[header.value] }}</slot> | ||||
|       <template | ||||
|         v-for="header in headersWithoutActions" | ||||
|         #[`item.${header.value}`]="{ item }" | ||||
|       > | ||||
|         <slot | ||||
|           :name="'item.' + header.value" | ||||
|           v-bind="{ item }" | ||||
|         > | ||||
|           {{ item[header.value] }} | ||||
|         </slot> | ||||
|       </template> | ||||
|       <template #item.actions="{ item }"> | ||||
|       <template #[`item.actions`]="{ item }"> | ||||
|         <BaseButtonGroup | ||||
|           :buttons="[ | ||||
|             { | ||||
|               icon: $globals.icons.edit, | ||||
|               text: $tc('general.edit'), | ||||
|               text: $t('general.edit'), | ||||
|               event: 'edit', | ||||
|             }, | ||||
|             { | ||||
|               icon: $globals.icons.delete, | ||||
|               text: $tc('general.delete'), | ||||
|               text: $t('general.delete'), | ||||
|               event: 'delete', | ||||
|             }, | ||||
|           ]" | ||||
| @@ -74,8 +95,11 @@ | ||||
|       </template> | ||||
|     </v-data-table> | ||||
|     <v-card-actions class="justify-end"> | ||||
|       <slot name="button-bottom"> </slot> | ||||
|       <BaseButton color="info" @click="downloadAsJson(data, 'export.json')"> | ||||
|       <slot name="button-bottom" /> | ||||
|       <BaseButton | ||||
|         color="info" | ||||
|         @click="downloadAsJson(data, 'export.json')" | ||||
|       > | ||||
|         <template #icon> | ||||
|           {{ $globals.icons.download }} | ||||
|         </template> | ||||
| @@ -86,7 +110,6 @@ | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { computed, defineComponent, ref } from "@nuxtjs/composition-api"; | ||||
| import { downloadAsJson } from "~/composables/use-utils"; | ||||
|  | ||||
| export interface TableConfig { | ||||
| @@ -109,7 +132,7 @@ export interface BulkAction { | ||||
|   event: string; | ||||
| } | ||||
|  | ||||
| export default defineComponent({ | ||||
| export default defineNuxtComponent({ | ||||
|   props: { | ||||
|     tableConfig: { | ||||
|       type: Object as () => TableConfig, | ||||
| @@ -139,28 +162,34 @@ export default defineComponent({ | ||||
|       default: false, | ||||
|     }, | ||||
|   }, | ||||
|   emits: ["delete-one", "edit-one"], | ||||
|   setup(props, context) { | ||||
|     const i18n = useI18n(); | ||||
|     const sortBy = computed(() => [{ | ||||
|       key: props.initialSort, | ||||
|       order: props.initialSortDesc ? "desc" : "asc", | ||||
|     }]); | ||||
|  | ||||
|     // =========================================================== | ||||
|     // Reactive Headers | ||||
|     const filteredHeaders = ref<string[]>([]); | ||||
|  | ||||
|     // Set default filtered | ||||
|     filteredHeaders.value = (() => { | ||||
|       const filtered: string[] = []; | ||||
|       props.headers.forEach((element) => { | ||||
|         if (element.show) { | ||||
|           filtered.push(element.value); | ||||
|         } | ||||
|       }); | ||||
|       return filtered; | ||||
|     })(); | ||||
|  | ||||
|     const activeHeaders = computed(() => { | ||||
|       const filtered = props.headers.filter((header) => filteredHeaders.value.includes(header.value)); | ||||
|       filtered.push({ text: "", value: "actions", show: true, align: "right" }); | ||||
|       return filtered; | ||||
|     const filteredHeaders = computed<string[]>(() => { | ||||
|       return props.headers.filter(header => header.show).map(header => header.value); | ||||
|     }); | ||||
|  | ||||
|     const headersWithoutActions = computed(() => | ||||
|       props.headers | ||||
|         .filter(header => filteredHeaders.value.includes(header.value)) | ||||
|         .map(header => ({ | ||||
|           ...header, | ||||
|           title: i18n.t(header.text), | ||||
|         })), | ||||
|     ); | ||||
|  | ||||
|     const activeHeaders = computed(() => [ | ||||
|       ...headersWithoutActions.value, | ||||
|       { title: "", value: "actions", show: true, align: "end" }, | ||||
|     ]); | ||||
|  | ||||
|     const selected = ref<any[]>([]); | ||||
|  | ||||
|     // =========================================================== | ||||
| @@ -183,8 +212,10 @@ export default defineComponent({ | ||||
|     const search = ref(""); | ||||
|  | ||||
|     return { | ||||
|       sortBy, | ||||
|       selected, | ||||
|       filteredHeaders, | ||||
|       headersWithoutActions, | ||||
|       activeHeaders, | ||||
|       bulkActionListener, | ||||
|       search, | ||||
| @@ -198,4 +229,7 @@ export default defineComponent({ | ||||
| .clip-width { | ||||
|   max-width: 400px; | ||||
| } | ||||
| .v-btn--disabled { | ||||
|   opacity: 0.5 !important; | ||||
| } | ||||
| </style> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user