1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-24 15:49:42 +02:00

refactor(frontend): ♻️ rewrite search componenets to typescript

This commit is contained in:
hay-kot 2021-08-08 20:52:44 -08:00
parent 1981e191be
commit bde885dc84
25 changed files with 826 additions and 113 deletions

View file

@ -126,7 +126,7 @@ export default {
default: null,
},
hardLimit: {
type: Number,
type: [String, Number],
default: 99999,
},
mobileCards: {

View file

@ -0,0 +1,111 @@
<template>
<div>
<slot>
<v-btn icon class="mt-n1" @click="dialog = true">
<v-icon :color="color">{{ $globals.icons.create }}</v-icon>
</v-btn>
</slot>
<v-dialog v-model="dialog" width="500">
<v-card>
<v-app-bar dense dark color="primary mb-2">
<v-icon large left class="mt-1">
{{ $globals.icons.tags }}
</v-icon>
<v-toolbar-title class="headline">
{{ title }}
</v-toolbar-title>
<v-spacer></v-spacer>
</v-app-bar>
<v-card-title> </v-card-title>
<v-form @submit.prevent="select">
<v-card-text>
<v-text-field v-model="itemName" dense :label="inputLabel" :rules="[rules.required]"></v-text-field>
</v-card-text>
<v-card-actions>
<BaseButton cancel @click="dialog = false" />
<v-spacer></v-spacer>
<BaseButton type="submit" create :disabled="!itemName" />
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { defineComponent } from "vue-demi";
import { useApiSingleton } from "~/composables/use-api";
const CREATED_ITEM_EVENT = "created-item";
export default defineComponent({
props: {
buttonText: {
type: String,
default: "Add",
},
value: {
type: String,
default: "",
},
color: {
type: String,
default: null,
},
tagDialog: {
type: Boolean,
default: true,
},
},
setup() {
const api = useApiSingleton();
return { api };
},
data() {
return {
dialog: false,
itemName: "",
rules: {
required: (val) => !!val || "A Name is Required",
},
};
},
computed: {
title() {
return this.tagDialog ? "Create a Tag" : "Create a Category";
},
inputLabel() {
return this.tagDialog ? "Tag Name" : "Category Name";
},
},
watch: {
dialog(val) {
if (!val) this.itemName = "";
},
},
methods: {
open() {
this.dialog = true;
},
async select() {
const newItem = await (async () => {
if (this.tagDialog) {
const newItem = await this.api.tags.createOne({ name: this.itemName });
return newItem;
} else {
const newItem = await this.api.categories.createOne({ name: this.itemName });
return newItem;
}
})();
this.$emit(CREATED_ITEM_EVENT, newItem);
this.dialog = false;
},
},
});
</script>
<style></style>

View file

@ -0,0 +1,150 @@
<template>
<v-autocomplete
v-model="selected"
:items="activeItems"
:value="value"
:label="inputLabel"
chips
deletable-chips
:dense="dense"
item-text="name"
persistent-hint
multiple
:hint="hint"
:solo="solo"
:return-object="returnObject"
:flat="flat"
@input="emitChange"
>
<template #selection="data">
<v-chip
:key="data.index"
class="ma-1"
:input-value="data.selected"
close
label
color="accent"
dark
@click:close="removeByIndex(data.index)"
>
{{ data.item.name || data.item }}
</v-chip>
</template>
<template #append-outer="">
<RecipeCategoryTagDialog v-if="showAdd" :tag-dialog="tagSelector" @created-item="pushToItem" />
</template>
</v-autocomplete>
</template>
<script>
import RecipeCategoryTagDialog from "./RecipeCategoryTagDialog";
import { useApiSingleton } from "~/composables/use-api";
import { useTags, useCategories } from "~/composables/use-tags-categories";
const MOUNTED_EVENT = "mounted";
export default {
components: {
RecipeCategoryTagDialog,
},
props: {
value: {
type: Array,
required: true,
},
solo: {
type: Boolean,
default: false,
},
dense: {
type: Boolean,
default: true,
},
returnObject: {
type: Boolean,
default: true,
},
tagSelector: {
type: Boolean,
default: false,
},
hint: {
type: String,
default: null,
},
showAdd: {
type: Boolean,
default: false,
},
showLabel: {
type: Boolean,
default: true,
},
},
setup() {
const api = useApiSingleton();
const { allTags, useAsyncGetAll: getAllTags } = useTags();
const { allCategories, useAsyncGetAll: getAllCategories } = useCategories();
getAllCategories();
getAllTags();
return { api, allTags, allCategories };
},
data() {
return {
selected: [],
};
},
computed: {
inputLabel() {
if (!this.showLabel) return null;
return this.tagSelector ? this.$t("tag.tags") : this.$t("recipe.categories");
},
activeItems() {
let ItemObjects = [];
if (this.tagSelector) ItemObjects = this.allTags;
else {
ItemObjects = this.allCategories;
}
if (this.returnObject) return ItemObjects;
else {
return ItemObjects.map((x) => x.name);
}
},
flat() {
if (this.selected) {
return this.selected.length > 0 && this.solo;
}
return false;
},
},
watch: {
value(val) {
this.selected = val;
},
},
mounted() {
this.$emit(MOUNTED_EVENT);
this.setInit(this.value);
},
methods: {
emitChange() {
this.$emit("input", this.selected);
},
setInit(val) {
this.selected = val;
},
removeByIndex(index) {
this.selected.splice(index, 1);
},
pushToItem(createdItem) {
createdItem = this.returnObject ? createdItem : createdItem.name;
this.selected.push(createdItem);
},
},
};
</script>

View file

@ -0,0 +1,57 @@
<template>
<v-toolbar dense flat>
<v-btn-toggle v-model="selected" tile group color="primary accent-3" mandatory @change="emitMulti">
<v-btn small :value="false">
{{ $t("search.include") }}
</v-btn>
<v-btn small :value="true">
{{ $t("search.exclude") }}
</v-btn>
</v-btn-toggle>
<v-spacer></v-spacer>
<v-btn-toggle v-model="match" tile group color="primary accent-3" mandatory @change="emitMulti">
<v-btn small :value="false">
{{ $t("search.and") }}
</v-btn>
<v-btn small :value="true">
{{ $t("search.or") }}
</v-btn>
</v-btn-toggle>
</v-toolbar>
</template>
<script lang="ts">
import { defineComponent } from "vue-demi";
type SelectionValue = "include" | "exclude" | "any";
export default defineComponent({
props: {
value: {
type: String as () => SelectionValue,
default: "include",
},
},
data() {
return {
selected: false,
match: false,
};
},
methods: {
emitChange() {
this.$emit("input", this.selected);
},
emitMulti() {
const updateData = {
exclude: this.selected,
matchAny: this.match,
};
this.$emit("update", updateData);
},
},
});
</script>
<style lang="scss" scoped></style>