Rewrite Recipe Editor Buttons Bar (#482)

* rewrite editor button row

* add context menu items to recipe page

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-06-10 18:32:03 -08:00 committed by GitHub
parent c175c8e9a0
commit ead02737ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 221 additions and 116 deletions

View File

@ -8,15 +8,23 @@
ref="deleteRecipieConfirm" ref="deleteRecipieConfirm"
v-on:confirm="deleteRecipe()" v-on:confirm="deleteRecipe()"
/> />
<v-menu offset-y top left> <v-menu
offset-y
left
:bottom="!menuTop"
:nudge-bottom="!menuTop ? '5' : '0'"
:top="menuTop"
:nudge-top="menuTop ? '5' : '0'"
allow-overflow
>
<template v-slot:activator="{ on, attrs }"> <template v-slot:activator="{ on, attrs }">
<v-btn color="primary" icon dark v-bind="attrs" v-on="on" @click.prevent> <v-btn :fab="fab" small="fab" :color="color" :icon="!fab" dark v-bind="attrs" v-on="on" @click.prevent>
<v-icon>{{ menuIcon }}</v-icon> <v-icon>{{ menuIcon }}</v-icon>
</v-btn> </v-btn>
</template> </template>
<v-list dense> <v-list dense>
<v-list-item <v-list-item
v-for="(item, index) in loggedIn ? userMenu : defaultMenu" v-for="(item, index) in loggedIn && cardMenu ? userMenu : defaultMenu"
:key="index" :key="index"
@click="menuAction(item.action)" @click="menuAction(item.action)"
> >
@ -39,6 +47,18 @@ export default {
ConfirmationDialog, ConfirmationDialog,
}, },
props: { props: {
menuTop: {
type: Boolean,
default: true,
},
fab: {
type: Boolean,
default: false,
},
color: {
type: String,
default: "primary",
},
slug: { slug: {
type: String, type: String,
}, },
@ -48,6 +68,10 @@ export default {
name: { name: {
type: String, type: String,
}, },
cardMenu: {
type: Boolean,
default: true,
},
}, },
computed: { computed: {
loggedIn() { loggedIn() {
@ -118,7 +142,7 @@ export default {
url: this.recipeURL, url: this.recipeURL,
}) })
.then(() => console.log("Successful share")) .then(() => console.log("Successful share"))
.catch(error => { .catch((error) => {
console.log("WebShareAPI not supported", error); console.log("WebShareAPI not supported", error);
this.updateClipboard(); this.updateClipboard();
}); });

View File

@ -1,97 +0,0 @@
<template>
<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-confirmation')"
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>{{ $globals.icons.delete }}</v-icon>
</v-btn>
<v-btn class="mr-2" fab dark small color="success" @click="save">
<v-icon>{{ $globals.icons.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>{{ $globals.icons.edit }}</v-icon>
</v-btn>
</template>
</v-toolbar>
</v-expand-transition>
</template>
<script>
import ConfirmationDialog from "@/components/UI/Dialogs/ConfirmationDialog.vue";
export default {
props: {
open: {
type: Boolean,
default: true,
},
},
components: {
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: {
editor() {
this.$emit("editor");
},
save() {
this.$emit("save");
},
updateScroll() {
this.scrollPosition = window.scrollY;
},
deleteRecipeConfrim() {
this.$refs.deleteRecipieConfirm.open();
},
deleteRecipe() {
this.$emit("delete");
},
json() {
this.$emit("json");
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,164 @@
<template>
<v-toolbar
rounded
height="0"
class="fixed-bar mt-0"
color="rgb(255, 0, 0, 0.0)"
flat
style="z-index: 2; position: sticky"
:class="{ 'fixed-bar-mobile': $vuetify.breakpoint.xs }"
>
<ConfirmationDialog
:title="$t('recipe.delete-recipe')"
:message="$t('recipe.delete-confirmation')"
color="error"
icon="mdi-alert-circle"
ref="deleteRecipieConfirm"
v-on:confirm="emitDelete()"
/>
<v-spacer></v-spacer>
<div v-if="!edit" class="custom-btn-group ma-1">
<v-btn
fab
small
class="mx-1"
color="info"
@click="
edit = true;
$emit('edit');
"
>
<v-icon> {{ $globals.icons.edit }} </v-icon>
</v-btn>
<ContextMenu
:menu-top="false"
:slug="slug"
:name="name"
menu-icon="mdi-dots-horizontal"
fab
color="info"
:card-menu="false"
/>
</div>
<div v-if="edit" class="custom-btn-group mb-">
<v-btn
v-for="(btn, index) in editorButtons"
:key="index"
:fab="$vuetify.breakpoint.xs"
:small="$vuetify.breakpoint.xs"
class="mx-1"
:color="btn.color"
@click="emitHandler(btn.event)"
>
<v-icon :left="!$vuetify.breakpoint.xs">{{ btn.icon }}</v-icon>
{{ $vuetify.breakpoint.xs ? "" : btn.text }}
</v-btn>
</div>
</v-toolbar>
</template>
<script>
import ConfirmationDialog from "@/components/UI/Dialogs/ConfirmationDialog.vue";
import ContextMenu from "@/components/Recipe/ContextMenu.vue";
const SAVE_EVENT = "save";
const DELETE_EVENT = "delete";
const CLOSE_EVENT = "close";
const JSON_EVENT = "json";
export default {
components: { ConfirmationDialog, ContextMenu },
props: {
slug: {
type: String,
},
name: {
type: String,
},
},
data() {
return {
edit: false,
editorButtons: [
{
text: "Delete",
icon: this.$globals.icons.delete,
event: DELETE_EVENT,
color: "error",
},
{
text: "JSON",
icon: "mdi-code-braces",
event: JSON_EVENT,
color: "accent",
},
{
text: "Close",
icon: "mdi-close",
event: CLOSE_EVENT,
color: undefined,
},
{
text: "Save",
icon: this.$globals.icons.save,
event: SAVE_EVENT,
color: "success",
},
],
};
},
methods: {
emitHandler(event) {
switch (event) {
case CLOSE_EVENT:
this.$emit(CLOSE_EVENT);
this.edit = false;
break;
case SAVE_EVENT:
this.$emit(SAVE_EVENT);
this.edit = false;
break;
case JSON_EVENT:
this.$emit(JSON_EVENT);
break;
case DELETE_EVENT:
this.$refs.deleteRecipieConfirm.open();
break;
default:
break;
}
},
emitDelete() {
this.$emit(DELETE_EVENT);
this.edit = false;
},
},
};
</script>
<style scoped>
.custom-btn-group {
flex: 0, 1, auto;
display: inline-flex;
}
.vertical {
flex-direction: column !important;
}
.sticky {
margin-left: auto;
position: fixed !important;
margin-top: 4.25rem;
}
.fixed-bar {
position: sticky;
position: -webkit-sticky; /* for Safari */
top: 4.5em;
z-index: 2;
}
.fixed-bar-mobile {
top: 1.5em !important;
}
</style>

View File

@ -10,7 +10,7 @@
</v-img> </v-img>
<br v-else /> <br v-else />
<EditorButtonRow @json="jsonEditor = true" @editor="jsonEditor = false" @save="createRecipe" /> <RecipePageActionMenu @json="jsonEditor = true" @edit="jsonEditor = false" @save="createRecipe" />
<div v-if="jsonEditor"> <div v-if="jsonEditor">
<!-- Probably not the best way, but it works! --> <!-- Probably not the best way, but it works! -->
@ -29,12 +29,12 @@ import { api } from "@/api";
import RecipeEditor from "@/components/Recipe/RecipeEditor"; import RecipeEditor from "@/components/Recipe/RecipeEditor";
import VJsoneditor from "v-jsoneditor"; import VJsoneditor from "v-jsoneditor";
import EditorButtonRow from "@/components/Recipe/EditorButtonRow"; import RecipePageActionMenu from "@/components/Recipe/RecipePageActionMenu";
export default { export default {
components: { components: {
VJsoneditor, VJsoneditor,
RecipeEditor, RecipeEditor,
EditorButtonRow, RecipePageActionMenu,
}, },
data() { data() {
return { return {

View File

@ -6,7 +6,7 @@
<NoRecipe v-else-if="loadFailed" /> <NoRecipe v-else-if="loadFailed" />
<v-card v-else-if="!loadFailed" id="myRecipe" class="d-print-none"> <v-card v-else-if="!loadFailed" id="myRecipe" class="d-print-none">
<v-img <v-img
:height="hideImage ? '40' : imageHeight" :height="hideImage ? '50' : imageHeight"
@error="hideImage = true" @error="hideImage = true"
:src="getImage(recipeDetails.slug)" :src="getImage(recipeDetails.slug)"
class="d-print-none" class="d-print-none"
@ -20,17 +20,20 @@
:performTime="recipeDetails.performTime" :performTime="recipeDetails.performTime"
/> />
</v-img> </v-img>
<EditorButtonRow <RecipePageActionMenu
:slug="recipeDetails.slug"
:name="recipeDetails.name"
v-if="loggedIn" v-if="loggedIn"
:open="showIcons" :open="showIcons"
@json="jsonEditor = true" @close="form = false"
@editor=" @json="jsonEditor = !jsonEditor"
@edit="
jsonEditor = false; jsonEditor = false;
form = true; form = true;
" "
@save="saveRecipe" @save="saveRecipe"
@delete="deleteRecipe" @delete="deleteRecipe"
class="sticky" class="ml-auto"
/> />
<RecipeViewer v-if="!form" :recipe="recipeDetails" /> <RecipeViewer v-if="!form" :recipe="recipeDetails" />
@ -42,7 +45,13 @@
height="1500px" height="1500px"
:options="jsonEditorOptions" :options="jsonEditorOptions"
/> />
<RecipeEditor v-else v-model="recipeDetails" ref="recipeEditor" @upload="getImageFile" /> <RecipeEditor
v-else
v-model="recipeDetails"
:class="$vuetify.breakpoint.xs ? 'mt-5' : undefiend"
ref="recipeEditor"
@upload="getImageFile"
/>
</v-card> </v-card>
<CommentsSection <CommentsSection
class="mt-2 d-print-none" class="mt-2 d-print-none"
@ -56,6 +65,7 @@
</template> </template>
<script> <script>
import RecipePageActionMenu from "@/components/Recipe/RecipePageActionMenu.vue";
import { api } from "@/api"; import { api } from "@/api";
import FavoriteBadge from "@/components/Recipe/FavoriteBadge"; import FavoriteBadge from "@/components/Recipe/FavoriteBadge";
import VJsoneditor from "v-jsoneditor"; import VJsoneditor from "v-jsoneditor";
@ -63,7 +73,6 @@ import RecipeViewer from "@/components/Recipe/RecipeViewer";
import PrintView from "@/components/Recipe/PrintView"; import PrintView from "@/components/Recipe/PrintView";
import RecipeEditor from "@/components/Recipe/RecipeEditor"; import RecipeEditor from "@/components/Recipe/RecipeEditor";
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue"; import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
import EditorButtonRow from "@/components/Recipe/EditorButtonRow.vue";
import NoRecipe from "@/components/Fallbacks/NoRecipe"; import NoRecipe from "@/components/Fallbacks/NoRecipe";
import { user } from "@/mixins/user"; import { user } from "@/mixins/user";
import { router } from "@/routes"; import { router } from "@/routes";
@ -74,8 +83,8 @@ export default {
VJsoneditor, VJsoneditor,
RecipeViewer, RecipeViewer,
RecipeEditor, RecipeEditor,
EditorButtonRow,
RecipeTimeCard, RecipeTimeCard,
RecipePageActionMenu,
PrintView, PrintView,
NoRecipe, NoRecipe,
FavoriteBadge, FavoriteBadge,
@ -126,15 +135,13 @@ export default {
this.jsonEditor = false; this.jsonEditor = false;
this.form = this.$route.query.edit === "true" && this.loggedIn; this.form = this.$route.query.edit === "true" && this.loggedIn;
if (this.$route.query.print) { this.checkPrintRecipe();
this.printPage();
this.$router.push(this.$route.path);
}
}, },
watch: { watch: {
$route: function () { $route: function () {
this.getRecipeDetails(); this.getRecipeDetails();
this.checkPrintRecipe();
}, },
}, },
@ -166,6 +173,13 @@ export default {
}, },
}, },
methods: { methods: {
checkPrintRecipe() {
if (this.$route.query.print) {
this.printPage();
this.$router.push(this.$route.path);
this.$route.query.print = null;
}
},
getImageFile(fileObject) { getImageFile(fileObject) {
this.fileObject = fileObject; this.fileObject = fileObject;
this.saveImage(); this.saveImage();