mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-21 05:53:36 -07:00
Use composition API for more components, enable more type checking (#914)
* Activate more linting rules from eslint and typescript * Properly add VForm as type information * Fix usage of native types * Fix more linting issues * Rename vuetify types file, add VTooltip * Fix some more typing problems * Use composition API for more components * Convert RecipeRating * Convert RecipeNutrition * Convert more components to composition API * Fix globals plugin for type checking * Add missing icon types * Fix vuetify types in Nuxt context * Use composition API for RecipeActionMenu * Convert error.vue to composition API * Convert RecipeContextMenu to composition API * Use more composition API and type checking in recipe/create * Convert AppButtonUpload to composition API * Fix some type checking in RecipeContextMenu * Remove unused components BaseAutoForm and BaseColorPicker * Convert RecipeCategoryTagDialog to composition API * Convert RecipeCardSection to composition API * Convert RecipeCategoryTagSelector to composition API * Properly import vuetify type definitions * Convert BaseButton to composition API * Convert AutoForm to composition API * Remove unused requests API file * Remove static routes from recipe API * Fix more type errors * Convert AppHeader to composition API, fixing some search bar focus problems * Convert RecipeDialogSearch to composition API * Update API types from pydantic models, handle undefined values * Improve more typing problems * Add types to other plugins * Properly type the CRUD API access * Fix typing of static image routes * Fix more typing stuff * Fix some more typing problems * Turn off more rules
This commit is contained in:
parent
d5ab5ec66f
commit
86c99b10a2
114 changed files with 2218 additions and 2033 deletions
|
@ -34,8 +34,11 @@
|
|||
</v-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "@nuxtjs/composition-api";
|
||||
import { VTooltip } from "~/types/vuetify";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
copyText: {
|
||||
type: String,
|
||||
|
@ -54,30 +57,34 @@ export default {
|
|||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
};
|
||||
},
|
||||
setup(props) {
|
||||
const show = ref(false);
|
||||
const copyToolTip = ref<VTooltip | null>(null);
|
||||
|
||||
methods: {
|
||||
toggleBlur() {
|
||||
this.$refs.copyToolTip.deactivate();
|
||||
},
|
||||
textToClipboard() {
|
||||
this.show = true;
|
||||
const copyText = this.copyText;
|
||||
function toggleBlur() {
|
||||
copyToolTip.value?.deactivate();
|
||||
}
|
||||
|
||||
function textToClipboard() {
|
||||
show.value = true;
|
||||
const copyText = props.copyText;
|
||||
navigator.clipboard.writeText(copyText).then(
|
||||
() => console.log(`Copied\n${copyText}`),
|
||||
() => console.log(`Copied Failed\n${copyText}`)
|
||||
);
|
||||
setTimeout(() => {
|
||||
this.toggleBlur();
|
||||
toggleBlur();
|
||||
}, 500);
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
show,
|
||||
copyToolTip,
|
||||
textToClipboard,
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
</v-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
|
||||
const UPLOAD_EVENT = "uploaded";
|
||||
export default {
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
small: {
|
||||
type: Boolean,
|
||||
|
@ -48,65 +51,70 @@ export default {
|
|||
default: "",
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
setup(props, context) {
|
||||
const file = ref<File | null>(null);
|
||||
const uploader = ref<HTMLInputElement | null>(null);
|
||||
const isSelecting = ref(false);
|
||||
|
||||
const { i18n, $globals } = useContext();
|
||||
const effIcon = props.icon ? props.icon : $globals.icons.upload;
|
||||
|
||||
const defaultText = i18n.t("general.upload");
|
||||
|
||||
const api = useUserApi();
|
||||
async function upload() {
|
||||
if (file.value != null) {
|
||||
isSelecting.value = true;
|
||||
|
||||
return { api };
|
||||
},
|
||||
data: () => ({
|
||||
file: null,
|
||||
isSelecting: false,
|
||||
}),
|
||||
|
||||
computed: {
|
||||
effIcon() {
|
||||
return this.icon ? this.icon : this.$globals.icons.upload;
|
||||
},
|
||||
defaultText() {
|
||||
return this.$t("general.upload");
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async upload() {
|
||||
if (this.file != null) {
|
||||
this.isSelecting = true;
|
||||
|
||||
if (!this.post) {
|
||||
this.$emit(UPLOAD_EVENT, this.file);
|
||||
this.isSelecting = false;
|
||||
if (!props.post) {
|
||||
context.emit(UPLOAD_EVENT, file.value);
|
||||
isSelecting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append(this.fileName, this.file);
|
||||
formData.append(props.fileName, file.value);
|
||||
|
||||
const response = await this.api.upload.file(this.url, formData);
|
||||
const response = await api.upload.file(props.url, formData);
|
||||
|
||||
if (response) {
|
||||
this.$emit(UPLOAD_EVENT, response);
|
||||
context.emit(UPLOAD_EVENT, response);
|
||||
}
|
||||
this.isSelecting = false;
|
||||
isSelecting.value = false;
|
||||
}
|
||||
},
|
||||
onButtonClick() {
|
||||
this.isSelecting = true;
|
||||
}
|
||||
|
||||
function onFileChanged(e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (target.files !== null && target.files.length > 0 && file.value !== null) {
|
||||
file.value = target.files[0];
|
||||
upload();
|
||||
}
|
||||
}
|
||||
|
||||
function onButtonClick() {
|
||||
isSelecting.value = true;
|
||||
window.addEventListener(
|
||||
"focus",
|
||||
() => {
|
||||
this.isSelecting = false;
|
||||
isSelecting.value = false;
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
uploader.value?.click();
|
||||
}
|
||||
|
||||
this.$refs.uploader.click();
|
||||
},
|
||||
onFileChanged(e) {
|
||||
this.file = e.target.files[0];
|
||||
this.upload();
|
||||
},
|
||||
return {
|
||||
file,
|
||||
uploader,
|
||||
isSelecting,
|
||||
effIcon,
|
||||
defaultText,
|
||||
onFileChanged,
|
||||
onButtonClick,
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
|
@ -40,15 +42,15 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
size() {
|
||||
if (this.small) {
|
||||
setup(props) {
|
||||
const size = computed(() => {
|
||||
if (props.small) {
|
||||
return {
|
||||
width: 2,
|
||||
icon: 30,
|
||||
size: 50,
|
||||
};
|
||||
} else if (this.large) {
|
||||
} else if (props.large) {
|
||||
return {
|
||||
width: 4,
|
||||
icon: 120,
|
||||
|
@ -60,10 +62,15 @@ export default {
|
|||
icon: 75,
|
||||
size: 125,
|
||||
};
|
||||
},
|
||||
waitingText() {
|
||||
return this.$t("general.loading-recipes");
|
||||
},
|
||||
});
|
||||
|
||||
const { i18n } = useContext();
|
||||
const waitingText = i18n.t("general.loading-recipes");
|
||||
|
||||
return {
|
||||
size,
|
||||
waitingText,
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -137,14 +137,15 @@
|
|||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from "@nuxtjs/composition-api";
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
||||
import { validators } from "@/composables/use-validators";
|
||||
import { fieldTypes } from "@/composables/forms";
|
||||
import { AutoFormItems } from "~/types/auto-forms";
|
||||
|
||||
const BLUR_EVENT = "blur";
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
name: "AutoForm",
|
||||
props: {
|
||||
value: {
|
||||
|
@ -157,7 +158,7 @@ export default {
|
|||
},
|
||||
items: {
|
||||
default: null,
|
||||
type: Array,
|
||||
type: Array as () => AutoFormItems,
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
|
@ -165,7 +166,7 @@ export default {
|
|||
},
|
||||
globalRules: {
|
||||
default: null,
|
||||
type: Array,
|
||||
type: Array as () => string[],
|
||||
},
|
||||
color: {
|
||||
default: null,
|
||||
|
@ -176,94 +177,53 @@ export default {
|
|||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const menu = ref({});
|
||||
setup(props, context) {
|
||||
function rulesByKey(keys?: string[] | null) {
|
||||
if (keys === undefined || keys === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
menu,
|
||||
fieldTypes,
|
||||
validators,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultRules() {
|
||||
return this.rulesByKey(this.globalRules);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
items: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
// Initialize Value Object to Obtain all keys
|
||||
if (!val) {
|
||||
return;
|
||||
const list = [] as ((v: string) => (boolean | string))[];
|
||||
keys.forEach((key) => {
|
||||
if (key in validators) {
|
||||
list.push(validators[key]);
|
||||
}
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
try {
|
||||
if (this.value[val[i].varName]) {
|
||||
continue;
|
||||
}
|
||||
} catch {}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
if (val[i].type === "text" || val[i].type === "textarea") {
|
||||
this.$set(this.value, val[i].varName, "");
|
||||
} else if (val[i].type === "select") {
|
||||
if (!val[i].options[0]) {
|
||||
continue;
|
||||
}
|
||||
const defaultRules = computed(() => rulesByKey(props.globalRules));
|
||||
|
||||
this.$set(this.value, val[i].varName, val[i].options[0].value);
|
||||
} else if (val[i].type === "list") {
|
||||
this.$set(this.value, val[i].varName, []);
|
||||
} else if (val[i].type === "object") {
|
||||
this.$set(this.value, val[i].varName, {});
|
||||
} else if (val[i].type === "color") {
|
||||
this.$set(this.value, val[i].varName, "");
|
||||
this.$set(this.menu, val[i].varName, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removeByIndex(list, index) {
|
||||
function removeByIndex(list: never[], index: number) {
|
||||
// Removes the item at the index
|
||||
list.splice(index, 1);
|
||||
},
|
||||
getTemplate(item) {
|
||||
const obj = {};
|
||||
}
|
||||
|
||||
function getTemplate(item: AutoFormItems) {
|
||||
const obj = {} as { [key: string]: string };
|
||||
|
||||
item.forEach((field) => {
|
||||
obj[field.varName] = "";
|
||||
});
|
||||
|
||||
return obj;
|
||||
},
|
||||
rulesByKey(keys) {
|
||||
const list = [];
|
||||
}
|
||||
|
||||
if (keys === undefined) {
|
||||
return list;
|
||||
}
|
||||
if (keys === null) {
|
||||
return list;
|
||||
}
|
||||
if (keys === list) {
|
||||
return list;
|
||||
}
|
||||
function emitBlur() {
|
||||
context.emit(BLUR_EVENT, props.value);
|
||||
}
|
||||
|
||||
keys.forEach((key) => {
|
||||
if (key in this.validators) {
|
||||
list.push(this.validators[key]);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
},
|
||||
emitBlur() {
|
||||
this.$emit(BLUR_EVENT, this.value);
|
||||
},
|
||||
return {
|
||||
rulesByKey,
|
||||
defaultRules,
|
||||
removeByIndex,
|
||||
getTemplate,
|
||||
emitBlur,
|
||||
fieldTypes,
|
||||
validators,
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
@ -1,269 +0,0 @@
|
|||
<template>
|
||||
<v-card :color="color" :dark="dark" flat :width="width" class="my-2">
|
||||
<v-row>
|
||||
<v-col v-for="(inputField, index) in items" :key="index" class="py-0" cols="12" sm="12">
|
||||
<v-divider v-if="inputField.section" class="my-2" />
|
||||
<v-card-title v-if="inputField.section" class="pl-0">
|
||||
{{ inputField.section }}
|
||||
</v-card-title>
|
||||
<v-card-text v-if="inputField.sectionDetails" class="pl-0 mt-0 pt-0">
|
||||
{{ inputField.sectionDetails }}
|
||||
</v-card-text>
|
||||
|
||||
<!-- Check Box -->
|
||||
<v-checkbox
|
||||
v-if="inputField.type === fieldTypes.BOOLEAN"
|
||||
v-model="value[inputField.varName]"
|
||||
class="my-0 py-0"
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:hint="inputField.hint || ''"
|
||||
@change="emitBlur"
|
||||
/>
|
||||
|
||||
<!-- Text Field -->
|
||||
<v-text-field
|
||||
v-else-if="inputField.type === fieldTypes.TEXT"
|
||||
v-model="value[inputField.varName]"
|
||||
:readonly="inputField.fixed && updateMode"
|
||||
filled
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
dense
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:hint="inputField.hint || ''"
|
||||
:rules="[...rulesByKey(inputField.rules), ...defaultRules]"
|
||||
lazy-validation
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
|
||||
<!-- Text Area -->
|
||||
<v-textarea
|
||||
v-else-if="inputField.type === fieldTypes.TEXT_AREA"
|
||||
v-model="value[inputField.varName]"
|
||||
:readonly="inputField.fixed && updateMode"
|
||||
filled
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
rows="3"
|
||||
auto-grow
|
||||
dense
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:hint="inputField.hint || ''"
|
||||
:rules="[...rulesByKey(inputField.rules), ...defaultRules]"
|
||||
lazy-validation
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
|
||||
<!-- Option Select -->
|
||||
<v-select
|
||||
v-else-if="inputField.type === fieldTypes.SELECT"
|
||||
v-model="value[inputField.varName]"
|
||||
:readonly="inputField.fixed && updateMode"
|
||||
filled
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
:prepend-icon="inputField.icons ? value[inputField.varName] : null"
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:items="inputField.options"
|
||||
:return-object="false"
|
||||
lazy-validation
|
||||
@blur="emitBlur"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ item.text }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ item.description }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<!-- Color Picker -->
|
||||
<div v-else-if="inputField.type === fieldTypes.COLOR" class="d-flex" style="width: 100%">
|
||||
<v-menu offset-y>
|
||||
<template #activator="{ on }">
|
||||
<v-btn class="my-2 ml-auto" style="min-width: 200px" :color="value[inputField.varName]" dark v-on="on">
|
||||
{{ inputField.label }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-color-picker
|
||||
v-model="value[inputField.varName]"
|
||||
value="#7417BE"
|
||||
hide-canvas
|
||||
hide-inputs
|
||||
show-swatches
|
||||
class="mx-auto"
|
||||
@input="emitBlur"
|
||||
/>
|
||||
</v-menu>
|
||||
</div>
|
||||
|
||||
<div v-else-if="inputField.type === fieldTypes.OBJECT">
|
||||
<base-auto-form
|
||||
v-model="value[inputField.varName]"
|
||||
:color="color"
|
||||
:items="inputField.items"
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- List Type -->
|
||||
<div v-else-if="inputField.type === fieldTypes.LIST">
|
||||
<div v-for="(item, idx) in value[inputField.varName]" :key="idx">
|
||||
<p>
|
||||
{{ inputField.label }} {{ idx + 1 }}
|
||||
<span>
|
||||
<BaseButton class="ml-5" x-small delete @click="removeByIndex(value[inputField.varName], idx)" />
|
||||
</span>
|
||||
</p>
|
||||
<v-divider class="mb-5 mx-2" />
|
||||
<base-auto-form
|
||||
v-model="value[inputField.varName][idx]"
|
||||
:color="color"
|
||||
:items="inputField.items"
|
||||
@blur="emitBlur"
|
||||
/>
|
||||
</div>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<BaseButton small @click="value[inputField.varName].push(getTemplate(inputField.items))"> New </BaseButton>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from "@nuxtjs/composition-api";
|
||||
import { validators } from "@/composables/use-validators";
|
||||
import { fieldTypes } from "@/composables/forms";
|
||||
|
||||
const BLUR_EVENT = "blur";
|
||||
|
||||
export default {
|
||||
name: "BaseAutoForm",
|
||||
props: {
|
||||
value: {
|
||||
default: null,
|
||||
type: [Object, Array],
|
||||
},
|
||||
updateMode: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
items: {
|
||||
default: null,
|
||||
type: Array,
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: "max",
|
||||
},
|
||||
globalRules: {
|
||||
default: null,
|
||||
type: Array,
|
||||
},
|
||||
color: {
|
||||
default: null,
|
||||
type: String,
|
||||
},
|
||||
dark: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const menu = ref({});
|
||||
|
||||
return {
|
||||
menu,
|
||||
fieldTypes,
|
||||
validators,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultRules() {
|
||||
return this.rulesByKey(this.globalRules);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
items: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
// Initialize Value Object to Obtain all keys
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
try {
|
||||
if (this.value[val[i].varName]) {
|
||||
continue;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (val[i].type === "text" || val[i].type === "textarea") {
|
||||
this.$set(this.value, val[i].varName, "");
|
||||
} else if (val[i].type === "select") {
|
||||
if (!val[i].options[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.$set(this.value, val[i].varName, val[i].options[0].value);
|
||||
} else if (val[i].type === "list") {
|
||||
this.$set(this.value, val[i].varName, []);
|
||||
} else if (val[i].type === "object") {
|
||||
this.$set(this.value, val[i].varName, {});
|
||||
} else if (val[i].type === "color") {
|
||||
this.$set(this.value, val[i].varName, "");
|
||||
this.$set(this.menu, val[i].varName, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
removeByIndex(list, index) {
|
||||
// Removes the item at the index
|
||||
list.splice(index, 1);
|
||||
},
|
||||
getTemplate(item) {
|
||||
const obj = {};
|
||||
|
||||
item.forEach((field) => {
|
||||
obj[field.varName] = "";
|
||||
});
|
||||
|
||||
return obj;
|
||||
},
|
||||
rulesByKey(keys) {
|
||||
const list = [];
|
||||
|
||||
if (keys === undefined) {
|
||||
return list;
|
||||
}
|
||||
if (keys === null) {
|
||||
return list;
|
||||
}
|
||||
if (keys === list) {
|
||||
return list;
|
||||
}
|
||||
|
||||
keys.forEach((key) => {
|
||||
if (key in this.validators) {
|
||||
list.push(this.validators[key]);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
},
|
||||
emitBlur() {
|
||||
this.$emit(BLUR_EVENT, this.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -28,9 +28,11 @@
|
|||
</v-btn>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
export default {
|
||||
|
||||
export default defineComponent({
|
||||
name: "BaseButton",
|
||||
props: {
|
||||
// Types
|
||||
|
@ -106,103 +108,99 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const api = useUserApi();
|
||||
|
||||
return { api };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
buttonOptions: {
|
||||
create: {
|
||||
text: "Create",
|
||||
icon: this.$globals.icons.createAlt,
|
||||
color: "success",
|
||||
},
|
||||
update: {
|
||||
text: "Update",
|
||||
icon: this.$globals.icons.edit,
|
||||
color: "success",
|
||||
},
|
||||
save: {
|
||||
text: "Save",
|
||||
icon: this.$globals.icons.save,
|
||||
color: "success",
|
||||
},
|
||||
edit: {
|
||||
text: "Edit",
|
||||
icon: this.$globals.icons.edit,
|
||||
color: "info",
|
||||
},
|
||||
delete: {
|
||||
text: "Delete",
|
||||
icon: this.$globals.icons.delete,
|
||||
color: "error",
|
||||
},
|
||||
cancel: {
|
||||
text: "Cancel",
|
||||
icon: this.$globals.icons.close,
|
||||
color: "grey",
|
||||
},
|
||||
download: {
|
||||
text: "Download",
|
||||
icon: this.$globals.icons.download,
|
||||
color: "info",
|
||||
},
|
||||
setup(props) {
|
||||
const { $globals } = useContext();
|
||||
const buttonOptions = {
|
||||
create: {
|
||||
text: "Create",
|
||||
icon: $globals.icons.createAlt,
|
||||
color: "success",
|
||||
},
|
||||
buttonStyles: {
|
||||
defaults: {
|
||||
text: false,
|
||||
outlined: false,
|
||||
},
|
||||
secondary: {
|
||||
text: false,
|
||||
outlined: true,
|
||||
},
|
||||
minor: {
|
||||
outlined: false,
|
||||
text: true,
|
||||
},
|
||||
update: {
|
||||
text: "Update",
|
||||
icon: $globals.icons.edit,
|
||||
color: "success",
|
||||
},
|
||||
save: {
|
||||
text: "Save",
|
||||
icon: $globals.icons.save,
|
||||
color: "success",
|
||||
},
|
||||
edit: {
|
||||
text: "Edit",
|
||||
icon: $globals.icons.edit,
|
||||
color: "info",
|
||||
},
|
||||
delete: {
|
||||
text: "Delete",
|
||||
icon: $globals.icons.delete,
|
||||
color: "error",
|
||||
},
|
||||
cancel: {
|
||||
text: "Cancel",
|
||||
icon: $globals.icons.close,
|
||||
color: "grey",
|
||||
},
|
||||
download: {
|
||||
text: "Download",
|
||||
icon: $globals.icons.download,
|
||||
color: "info",
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
btnAttrs() {
|
||||
if (this.delete) {
|
||||
return this.buttonOptions.delete;
|
||||
} else if (this.update) {
|
||||
return this.buttonOptions.update;
|
||||
} else if (this.edit) {
|
||||
return this.buttonOptions.edit;
|
||||
} else if (this.cancel) {
|
||||
this.setMinor();
|
||||
return this.buttonOptions.cancel;
|
||||
} else if (this.save) {
|
||||
return this.buttonOptions.save;
|
||||
} else if (this.download) {
|
||||
return this.buttonOptions.download;
|
||||
|
||||
const btnAttrs = computed(() => {
|
||||
if (props.delete) {
|
||||
return buttonOptions.delete;
|
||||
} else if (props.update) {
|
||||
return buttonOptions.update;
|
||||
} else if (props.edit) {
|
||||
return buttonOptions.edit;
|
||||
} else if (props.cancel) {
|
||||
return buttonOptions.cancel;
|
||||
} else if (props.save) {
|
||||
return buttonOptions.save;
|
||||
} else if (props.download) {
|
||||
return buttonOptions.download;
|
||||
}
|
||||
return this.buttonOptions.create;
|
||||
},
|
||||
btnStyle() {
|
||||
if (this.secondary) {
|
||||
return this.buttonStyles.secondary;
|
||||
} else if (this.minor) {
|
||||
return this.buttonStyles.minor;
|
||||
return buttonOptions.create;
|
||||
});
|
||||
|
||||
const buttonStyles = {
|
||||
defaults: {
|
||||
text: false,
|
||||
outlined: false,
|
||||
},
|
||||
secondary: {
|
||||
text: false,
|
||||
outlined: true,
|
||||
},
|
||||
minor: {
|
||||
text: true,
|
||||
outlined: false,
|
||||
},
|
||||
};
|
||||
|
||||
const btnStyle = computed(() => {
|
||||
if (props.secondary) {
|
||||
return buttonStyles.secondary;
|
||||
} else if (props.minor || props.cancel) {
|
||||
return buttonStyles.minor;
|
||||
}
|
||||
return this.buttonStyles.defaults;
|
||||
},
|
||||
return buttonStyles.defaults;
|
||||
});
|
||||
|
||||
|
||||
const api = useUserApi();
|
||||
function downloadFile() {
|
||||
api.utils.download(props.downloadUrl);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
btnAttrs,
|
||||
btnStyle,
|
||||
downloadFile,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
setMinor() {
|
||||
this.buttonStyles.defaults = this.buttonStyles.minor;
|
||||
},
|
||||
setSecondary() {
|
||||
this.buttonStyles.defaults = this.buttonStyles.secondary;
|
||||
},
|
||||
downloadFile() {
|
||||
this.api.utils.download(this.downloadUrl);
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
|
@ -39,5 +41,5 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<h3>{{ buttonText }}</h3>
|
||||
</div>
|
||||
<v-text-field v-model="color" hide-details class="ma-0 pa-0" solo>
|
||||
<template #append>
|
||||
<v-menu v-model="menu" top nudge-bottom="105" nudge-left="16" :close-on-content-click="false">
|
||||
<template #activator="{ on }">
|
||||
<div :style="swatchStyle" swatches-max-height="300" v-on="on" />
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-text class="pa-0">
|
||||
<v-color-picker v-model="color" flat mode="hexa" show-swatches />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: "Choose a color",
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: "#ff0000",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
swatches: false,
|
||||
color: this.value || "#1976D2",
|
||||
mask: "!#XXXXXXXX",
|
||||
menu: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
swatchStyle() {
|
||||
const { value, menu } = this;
|
||||
return {
|
||||
backgroundColor: value,
|
||||
cursor: "pointer",
|
||||
height: "30px",
|
||||
width: "30px",
|
||||
borderRadius: menu ? "50%" : "4px",
|
||||
transition: "border-radius 200ms ease-in-out",
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
color() {
|
||||
this.updateColor();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateColor() {
|
||||
this.$emit("input", this.color);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
|
@ -110,7 +110,7 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const dialog = computed<Boolean>({
|
||||
const dialog = computed<boolean>({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
|
@ -129,12 +129,9 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
determineClose(): Boolean {
|
||||
determineClose(): boolean {
|
||||
return this.submitted && !this.loading && !this.keepOpen;
|
||||
},
|
||||
displayicon(): Boolean {
|
||||
return this.icon || this.$globals.icons.user;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
determineClose() {
|
||||
|
@ -181,4 +178,4 @@ export default defineComponent({
|
|||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
<v-divider :width="width" :class="color" :style="`border-width: ${thickness} !important`" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
width: {
|
||||
type: String,
|
||||
|
@ -18,5 +20,5 @@ export default {
|
|||
default: "accent",
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -17,13 +17,15 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
divider: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
w<template>
|
||||
<template>
|
||||
<v-card v-bind="$attrs" :class="classes" class="v-card--material pa-3">
|
||||
<div class="d-flex grow flex-wrap">
|
||||
<slot name="avatar">
|
||||
|
@ -40,8 +40,10 @@ w<template>
|
|||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
name: "MaterialCard",
|
||||
|
||||
props: {
|
||||
|
@ -70,22 +72,25 @@ export default {
|
|||
default: "",
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { $vuetify } = useContext();
|
||||
|
||||
computed: {
|
||||
classes() {
|
||||
const hasHeading = computed(() => false);
|
||||
const hasAltHeading = computed(() => false);
|
||||
const classes = computed(() => {
|
||||
return {
|
||||
"v-card--material--has-heading": this.hasHeading,
|
||||
"mt-3": this.$vuetify.breakpoint.name === "xs" || this.$vuetify.breakpoint.name === "sm",
|
||||
"v-card--material--has-heading": hasHeading,
|
||||
"mt-3": $vuetify.breakpoint.name === "xs" || $vuetify.breakpoint.name === "sm",
|
||||
};
|
||||
},
|
||||
hasHeading() {
|
||||
return false;
|
||||
},
|
||||
hasAltHeading() {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
hasHeading,
|
||||
hasAltHeading,
|
||||
classes,
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
></VJsoneditor>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
// @ts-ignore
|
||||
import VJsoneditor from "v-jsoneditor";
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
components: { VJsoneditor },
|
||||
props: {
|
||||
value: {
|
||||
|
@ -23,6 +25,6 @@ export default {
|
|||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ export default defineComponent({
|
|||
{ text: "Delete", value: "actions" },
|
||||
];
|
||||
|
||||
function handleRowClick(item: any) {
|
||||
function handleRowClick(item: ReportSummary) {
|
||||
router.push("/user/group/data/reports/" + item.id);
|
||||
}
|
||||
|
||||
|
@ -70,4 +70,4 @@ export default defineComponent({
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
</style>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue