feature/editor-improvements (#289)

* pin editor buttons on scroll

* scaler scratch

* fix langauge assignment 1st pass

* set lang on navigate

* refactor/breakup router

* unify style for language selectro

* refactor/code-cleanup

* refactor/page specific components to page folder

* Fix time card layout issue

* fix timecard display

* update mobile cards / fix overflow errors

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden
2021-04-21 21:52:12 -08:00
committed by GitHub
parent a5306c31c6
commit 284df44209
66 changed files with 778 additions and 664 deletions

View File

@@ -1,42 +1,51 @@
<template>
<v-toolbar class="card-btn" flat height="0" extension-height="0">
<template v-slot:extension>
<v-col></v-col>
<div v-if="open">
<v-btn
class="mr-2"
fab
dark
small
color="error"
@click="deleteRecipeConfrim"
>
<v-icon>mdi-delete</v-icon>
<v-expand-transition>
<v-toolbar
class="card-btn pt-1"
flat
:height="isSticky ? null : '0'"
:extension-height="isSticky ? '20' : '0'"
color="rgb(255, 0, 0, 0.0)"
>
<ConfirmationDialog
:title="$t('recipe.delete-recipe')"
:message="$t('recipe.delete-ConfirmationDialog')"
color="error"
icon="mdi-alert-circle"
ref="deleteRecipieConfirm"
v-on:confirm="deleteRecipe()"
/>
<template v-slot:extension>
<v-col></v-col>
<div v-if="open">
<v-btn
class="mr-2"
fab
dark
small
color="error"
@click="deleteRecipeConfrim"
>
<v-icon>mdi-delete</v-icon>
</v-btn>
<v-btn class="mr-2" fab dark small color="success" @click="save">
<v-icon>mdi-content-save</v-icon>
</v-btn>
<v-btn class="mr-5" fab dark small color="secondary" @click="json">
<v-icon>mdi-code-braces</v-icon>
</v-btn>
</div>
<v-btn color="accent" fab dark small @click="editor">
<v-icon>mdi-square-edit-outline</v-icon>
</v-btn>
<Confirmation
:title="$t('recipe.delete-recipe')"
:message="$t('recipe.delete-confirmation')"
color="error"
icon="mdi-alert-circle"
ref="deleteRecipieConfirm"
v-on:confirm="deleteRecipe()"
/>
<v-btn class="mr-2" fab dark small color="success" @click="save">
<v-icon>mdi-content-save</v-icon>
</v-btn>
<v-btn class="mr-5" fab dark small color="secondary" @click="json">
<v-icon>mdi-code-braces</v-icon>
</v-btn>
</div>
<v-btn color="accent" fab dark small @click="editor">
<v-icon>mdi-square-edit-outline</v-icon>
</v-btn>
</template>
</v-toolbar>
</template>
</v-toolbar>
</v-expand-transition>
</template>
<script>
import Confirmation from "../../components/UI/Confirmation.vue";
import ConfirmationDialog from "@/components/UI/Dialogs/ConfirmationDialog.vue";
export default {
props: {
@@ -47,7 +56,25 @@ export default {
},
components: {
Confirmation,
ConfirmationDialog,
},
data() {
return {
stickyTop: 50,
scrollPosition: null,
};
},
mounted() {
window.addEventListener("scroll", this.updateScroll);
},
destroy() {
window.removeEventListener("scroll", this.updateScroll);
},
computed: {
isSticky() {
return this.scrollPosition >= 500;
},
},
methods: {
@@ -57,6 +84,9 @@ export default {
save() {
this.$emit("save");
},
updateScroll() {
this.scrollPosition = window.scrollY;
},
deleteRecipeConfrim() {
this.$refs.deleteRecipieConfirm.open();

View File

@@ -1,23 +1,39 @@
<template>
<v-card
class="mx-auto"
hover
:to="`/recipe/${slug}`"
max-height="125"
@click="$emit('selected')"
>
<v-list-item>
<v-list-item-avatar rounded size="125" class="mt-0 ml-n4">
<v-img :src="getImage(slug)"> </v-img>
</v-list-item-avatar>
<v-list-item-content class="align-self-start">
<v-list-item-title>
{{ name }}
</v-list-item-title>
<v-rating length="5" size="16" dense :value="rating"></v-rating>
<div class="text">
<v-list-item-action-text>
{{ description | truncate(115) }}
</v-list-item-action-text>
<v-list-item three-line>
<v-list-item-avatar
tile
size="125"
color="grey"
class="v-mobile-img rounded-sm my-0 ml-n4"
>
<v-img :src="getImage(slug)" lazy-src=""></v-img
></v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class=" mb-1">{{ name }}</v-list-item-title>
<v-list-item-subtitle> {{ description }} </v-list-item-subtitle>
<div class="d-flex justify-center align-center">
<RecipeChips
:items="tags"
:title="false"
:limit="1"
:small="true"
:isCategory="false"
/>
<v-rating
color="secondary"
class="ml-auto"
background-color="secondary lighten-3"
dense
length="5"
size="15"
:value="rating"
></v-rating>
</div>
</v-list-item-content>
</v-list-item>
@@ -25,8 +41,12 @@
</template>
<script>
import RecipeChips from "@/components/Recipe/RecipeViewer/RecipeChips";
import { api } from "@/api";
export default {
components: {
RecipeChips,
},
props: {
name: String,
slug: String,
@@ -36,6 +56,9 @@ export default {
route: {
default: true,
},
tags: {
default: true,
},
},
methods: {
@@ -47,6 +70,11 @@ export default {
</script>
<style>
.v-mobile-img {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
.v-card--reveal {
align-items: center;
bottom: 0;

View File

@@ -11,7 +11,7 @@
<div>
Recipe Image
</div>
<UploadBtn
<TheUploadBtn
class="ml-auto"
url="none"
file-name="image"
@@ -44,12 +44,12 @@
<script>
const REFRESH_EVENT = "refresh";
const UPLOAD_EVENT = "upload";
import UploadBtn from "@/components/UI/UploadBtn";
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
import { api } from "@/api";
// import axios from "axios";
export default {
components: {
UploadBtn,
TheUploadBtn,
},
props: {
slug: String,

View File

@@ -33,7 +33,7 @@
class="my-3"
:label="$t('recipe.recipe-name')"
v-model="value.name"
:rules="[rules.required]"
:rules="[existsRule]"
>
</v-text-field>
<v-textarea
@@ -94,7 +94,7 @@
class="mr-n1"
slot="prepend"
color="error"
@click="removeIngredient(index)"
@click="removeByIndex(value.recipeIngredient, index)"
>
mdi-delete
</v-icon>
@@ -107,7 +107,7 @@
<v-btn color="secondary" fab dark small @click="addIngredient">
<v-icon>mdi-plus</v-icon>
</v-btn>
<BulkAdd @bulk-data="appendIngredients" />
<BulkAdd @bulk-data="addIngredient" />
<h2 class="mt-6">{{ $t("recipe.categories") }}</h2>
<CategoryTagSelector
@@ -140,7 +140,7 @@
color="white"
class="mr-2"
elevation="0"
@click="removeNote(index)"
@click="removeByIndex(value.notes, index)"
>
<v-icon color="error">mdi-delete</v-icon>
</v-btn>
@@ -183,7 +183,7 @@
color="white"
class="mr-2"
elevation="0"
@click="removeStep(index)"
@click="removeByIndex(value.recipeInstructions, index)"
>
<v-icon size="24" color="error">mdi-delete</v-icon>
</v-btn>
@@ -218,6 +218,7 @@
</template>
<script>
const UPLOAD_EVENT = "upload";
import draggable from "vuedraggable";
import utils from "@/utils";
import BulkAdd from "./BulkAdd";
@@ -225,6 +226,7 @@ import ExtrasEditor from "./ExtrasEditor";
import CategoryTagSelector from "@/components/FormHelpers/CategoryTagSelector";
import NutritionEditor from "./NutritionEditor";
import ImageUploadBtn from "./ImageUploadBtn.vue";
import { validators } from "@/mixins/validators";
export default {
components: {
BulkAdd,
@@ -237,26 +239,20 @@ export default {
props: {
value: Object,
},
mixins: [validators],
data() {
return {
drag: false,
fileObject: null,
rules: {
required: v => !!v || this.$i18n.t("recipe.key-name-required"),
whiteSpace: v =>
!v ||
v.split(" ").length <= 1 ||
this.$i18n.t("recipe.no-white-space-allowed"),
},
};
},
methods: {
uploadImage(fileObject) {
this.$emit("upload", fileObject);
this.$emit(UPLOAD_EVENT, fileObject);
},
toggleDisabled(stepIndex) {
if (this.disabledSteps.includes(stepIndex)) {
let index = this.disabledSteps.indexOf(stepIndex);
const index = this.disabledSteps.indexOf(stepIndex);
if (index !== -1) {
this.disabledSteps.splice(index, 1);
}
@@ -265,66 +261,40 @@ export default {
}
},
isDisabled(stepIndex) {
if (this.disabledSteps.includes(stepIndex)) {
return "disabled-card";
} else {
return;
}
return this.disabledSteps.includes(stepIndex) ? "disabled-card" : null;
},
generateKey(item, index) {
return utils.generateUniqueKey(item, index);
},
appendIngredients(ingredients) {
this.value.recipeIngredient.push(...ingredients);
},
addIngredient() {
let list = this.value.recipeIngredient;
list.push("");
},
removeIngredient(index) {
this.value.recipeIngredient.splice(index, 1);
addIngredient(ingredients = null) {
if (ingredients) {
this.value.recipeIngredient.push(...ingredients);
} else {
this.value.recipeIngredient.push("");
}
},
appendSteps(steps) {
let processSteps = [];
steps.forEach(element => {
processSteps.push({ text: element });
});
this.value.recipeInstructions.push(...processSteps);
this.value.recipeInstructions.push(
...steps.map(x => ({
text: x,
}))
);
},
addStep() {
let list = this.value.recipeInstructions;
list.push({ text: "" });
this.value.recipeInstructions.push({ text: "" });
},
removeStep(index) {
this.value.recipeInstructions.splice(index, 1);
},
addNote() {
let list = this.value.notes;
list.push({ text: "" });
},
removeNote(index) {
this.value.notes.splice(index, 1);
},
removeCategory(index) {
this.value.recipeCategory.splice(index, 1);
},
removeTags(index) {
this.value.tags.splice(index, 1);
this.value.notes.push({ text: "" });
},
saveExtras(extras) {
this.value.extras = extras;
},
removeByIndex(list, index) {
list.splice(index, 1);
},
validateRecipe() {
if (this.$refs.form.validate()) {
return true;
} else {
return false;
}
return this.$refs.form.validate();
},
},
};

View File

@@ -1,57 +1,26 @@
<template>
<v-card
color="accent"
class="custom-transparent d-flex justify-start align-center text-center "
class="custom-transparent d-flex justify-start align-center text-center time-card-flex"
tile
:width="`${timeCardWidth}`"
height="55"
v-if="totalTime || prepTime || performTime"
v-if="showCards"
>
<v-card flat color="rgb(255, 0, 0, 0.0)">
<v-icon large color="white" class="mx-2"> mdi-clock-outline </v-icon>
</v-card>
<v-divider vertical color="white" class="py-1" v-if="totalTime">
</v-divider>
<v-card flat color="rgb(255, 0, 0, 0.0)" class=" my-2 " v-if="totalTime">
<v-card-text class="white--text">
<div>
<strong> {{ $t("recipe.total-time") }} </strong>
</div>
<div>{{ totalTime }}</div>
</v-card-text>
</v-card>
<v-divider vertical color="white" class="py-1" v-if="prepTime"> </v-divider>
<v-card
v-for="(time, index) in allTimes"
:key="index"
class="d-flex justify-start align-center text-center time-card-flex"
flat
color="rgb(255, 0, 0, 0.0)"
class="white--text my-2 "
v-if="prepTime"
>
<v-card-text class="white--text">
<v-card-text class="caption white--text py-2">
<div>
<strong> {{ $t("recipe.prep-time") }} </strong>
<strong> {{ time.name }} </strong>
</div>
<div>{{ prepTime }}</div>
</v-card-text>
</v-card>
<v-divider vertical color="white" class="my-1" v-if="performTime">
</v-divider>
<v-card
flat
color="rgb(255, 0, 0, 0.0)"
class="white--text py-2 "
v-if="performTime"
>
<v-card-text class="white--text">
<div>
<strong> {{ $t("recipe.perform-time") }} </strong>
</div>
<div>{{ performTime }}</div>
<div>{{ time.value }}</div>
</v-card-text>
</v-card>
</v-card>
@@ -64,52 +33,52 @@ export default {
totalTime: String,
performTime: String,
},
watch: {
showCards(val) {
console.log(val);
},
},
computed: {
timeLength() {
let times = [];
let timeArray = [this.totalTime, this.prepTime, this.performTime];
timeArray.forEach(element => {
if (element) {
times.push(element);
}
});
return times.length;
showCards() {
return [this.prepTime, this.totalTime, this.performTime].some(
x => !this.isEmpty(x)
);
},
iconColumn() {
switch (this.timeLength) {
case 0:
return null;
case 1:
return 4;
case 2:
return 3;
case 3:
return 2;
default:
return 1;
}
allTimes() {
return [
this.validateTotalTime,
this.validatePrepTime,
this.validatePerformTime,
].filter(x => x !== null);
},
timeCardWidth() {
let timeArray = [this.totalTime, this.prepTime, this.performTime];
let width = 80;
timeArray.forEach(element => {
if (element) {
width += 95;
}
});
if (this.$vuetify.breakpoint.name === "xs") {
return "100%";
}
return `${width}px`;
validateTotalTime() {
return !this.isEmpty(this.totalTime)
? { name: this.$t("recipe.total-time"), value: this.totalTime }
: null;
},
validatePrepTime() {
return !this.isEmpty(this.prepTime)
? { name: this.$t("recipe.prep-time"), value: this.prepTime }
: null;
},
validatePerformTime() {
return !this.isEmpty(this.performTime)
? { name: this.$t("recipe.perform-time"), value: this.performTime }
: null;
},
},
methods: {
isEmpty(str) {
return !str || str.length === 0;
},
},
};
</script>
<style scoped>
.time-card-flex {
width: fit-content;
}
.custom-transparent {
opacity: 0.7;
}