feature/mobile-layout (#431)

* lazy load cards

* shopping list recipe search bug

* admin layout fluid

* site loader

* username support

* mobile tabs

* set username at signup

* update user tests

* patch bug on shopping list

* public mealplan links

* support link (I'm a monster)

* icon only on mobile

* padding

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden
2021-05-25 21:01:22 -07:00
committed by GitHub
parent 8f8127a5fc
commit 822663905d
33 changed files with 273 additions and 119 deletions

View File

@@ -24,7 +24,7 @@
</div>
</v-row>
</v-card-text>
<v-tabs v-model="tab">
<v-tabs v-model="tab" show-arrows="">
<v-tab>{{ $t("general.recipes") }}</v-tab>
<v-tab>{{ $t("general.themes") }}</v-tab>
<v-tab>{{ $t("general.settings") }}</v-tab>

View File

@@ -22,7 +22,7 @@
v-model="user.email"
prepend-icon="mdi-email"
validate-on-blur
:label="$t('user.email')"
:label="`${$t('user.email')} or ${$t('user.username')} `"
type="email"
></v-text-field>
<v-text-field

View File

@@ -21,8 +21,15 @@
:prepend-icon="$globals.icons.user"
validate-on-blur
:rules="[existsRule]"
:label="$t('signup.display-name')"
type="email"
:label="$t('user.full-name')"
></v-text-field>
<v-text-field
v-model="user.username"
light="light"
:prepend-icon="$globals.icons.user"
validate-on-blur
:rules="[existsRule]"
:label="$t('user.username')"
></v-text-field>
<v-text-field
v-model="user.email"
@@ -111,6 +118,7 @@ export default {
const userData = {
fullName: this.user.name,
username: this.user.username,
email: this.user.email,
group: "default",
password: this.user.password,

View File

@@ -27,7 +27,7 @@
<v-icon left dark>
mdi-clipboard-check
</v-icon>
{{ $t("general.coppied") }}!
<slot> {{ $t("general.coppied") }}! </slot>
</span>
</v-tooltip>
</template>

View File

@@ -1,24 +1,24 @@
<template>
<div v-if="recipes">
<v-app-bar color="transparent" flat class="mt-n1 rounded" v-if="!disableToolbar">
<v-app-bar color="transparent" flat class="mt-n1 flex-sm-wrap rounded " v-if="!disableToolbar">
<v-icon large left v-if="title">
{{ displayTitleIcon }}
</v-icon>
<v-toolbar-title class="headline"> {{ title }} </v-toolbar-title>
<v-spacer></v-spacer>
<v-btn text @click="navigateRandom">
<v-icon left>
<v-btn :icon="$vuetify.breakpoint.xsOnly" text @click="navigateRandom">
<v-icon :left="!$vuetify.breakpoint.xsOnly">
mdi-dice-multiple
</v-icon>
{{ $t("general.random") }}
{{ $vuetify.breakpoint.xsOnly ? null : $t("general.random") }}
</v-btn>
<v-menu offset-y left v-if="$listeners.sort">
<template v-slot:activator="{ on, attrs }">
<v-btn text v-bind="attrs" v-on="on" :loading="sortLoading">
<v-icon left>
<v-btn text :icon="$vuetify.breakpoint.xsOnly" v-bind="attrs" v-on="on" :loading="sortLoading">
<v-icon :left="!$vuetify.breakpoint.xsOnly">
mdi-sort
</v-icon>
{{ $t("general.sort") }}
{{ $vuetify.breakpoint.xsOnly ? null : $t("general.sort") }}
</v-btn>
</template>
<v-list>
@@ -58,14 +58,16 @@
<div v-if="recipes" class="mt-2">
<v-row v-if="!viewScale">
<v-col :sm="6" :md="6" :lg="4" :xl="3" v-for="recipe in recipes.slice(0, cardLimit)" :key="recipe.name">
<RecipeCard
:name="recipe.name"
:description="recipe.description"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:tags="recipe.tags"
/>
<v-lazy>
<RecipeCard
:name="recipe.name"
:description="recipe.description"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:tags="recipe.tags"
/>
</v-lazy>
</v-col>
</v-row>
<v-row v-else dense>
@@ -78,33 +80,29 @@
v-for="recipe in recipes.slice(0, cardLimit)"
:key="recipe.name"
>
<MobileRecipeCard
:name="recipe.name"
:description="recipe.description"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:tags="recipe.tags"
/>
<v-lazy>
<MobileRecipeCard
:name="recipe.name"
:description="recipe.description"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:tags="recipe.tags"
/>
</v-lazy>
</v-col>
</v-row>
</div>
<div v-intersect="bumpList" class="d-flex">
<v-expand-x-transition>
<v-progress-circular
v-if="loading"
class="mx-auto mt-1"
:size="50"
:width="7"
color="primary"
indeterminate
></v-progress-circular>
<SiteLoader v-if="loading" :loading="loading" :size="150" />
</v-expand-x-transition>
</div>
</div>
</template>
<script>
import SiteLoader from "@/components/UI/SiteLoader";
import RecipeCard from "../Recipe/RecipeCard";
import MobileRecipeCard from "@/components/Recipe/MobileRecipeCard";
import { utils } from "@/utils";
@@ -114,6 +112,7 @@ export default {
components: {
RecipeCard,
MobileRecipeCard,
SiteLoader,
},
props: {
disableToolbar: {
@@ -139,7 +138,7 @@ export default {
data() {
return {
sortLoading: false,
cardLimit: 30,
cardLimit: 50,
loading: false,
EVENTS: {
az: "az",

View File

@@ -43,25 +43,31 @@
</div>
<router-link to="/search"> Advanced Search </router-link>
</v-card-actions>
<MobileRecipeCard
v-for="(recipe, index) in results.slice(0, 10)"
:tabindex="index"
:key="index"
class="ma-1 arrow-nav"
:name="recipe.name"
:description="recipe.description"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:route="true"
v-on="$listeners.selected ? { selected: () => grabRecipe(recipe) } : {}"
/>
<v-card-actions v-if="loading">
<SiteLoader :loading="loading" />
</v-card-actions>
<div v-else>
<MobileRecipeCard
v-for="(recipe, index) in results.slice(0, 10)"
:tabindex="index"
:key="index"
class="ma-1 arrow-nav"
:name="recipe.name"
:description="recipe.description"
:slug="recipe.slug"
:rating="recipe.rating"
:image="recipe.image"
:route="true"
v-on="$listeners.selected ? { selected: () => grabRecipe(recipe) } : {}"
/>
</div>
</v-card>
</v-dialog>
</div>
</template>
<script>
import SiteLoader from "@/components/UI/SiteLoader";
const SELECTED_EVENT = "selected";
import FuseSearchBar from "@/components/UI/Search/FuseSearchBar";
import MobileRecipeCard from "@/components/Recipe/MobileRecipeCard";
@@ -69,9 +75,11 @@ export default {
components: {
FuseSearchBar,
MobileRecipeCard,
SiteLoader,
},
data() {
return {
loading: false,
selectedIndex: -1,
dialog: false,
searchString: "",
@@ -82,14 +90,17 @@ export default {
$route() {
this.dialog = false;
},
dialog(val) {
async dialog(val) {
if (!val) {
this.resetSelected();
} else if (this.allItems.length <= 0) {
this.loading = true;
await this.$store.dispatch("requestAllRecipes");
this.loading = false;
}
},
},
mounted() {
this.$store.dispatch("requestAllRecipes");
document.addEventListener("keydown", this.onUpDown);
},
beforeDestroy() {

View File

@@ -0,0 +1,25 @@
<template>
<v-progress-circular class="mx-auto" :width="size / 20" :size="size" color="primary lighten-2" indeterminate>
<div class="text-center">
<v-icon :size="size / 2" color="primary lighten-2">
{{ $globals.icons.primary }}
</v-icon>
<div>
Loading Recipes
</div>
</div>
</v-progress-circular>
</template>
<script>
export default {
props: {
loading: {
default: true,
},
size: {
default: 200,
},
},
};
</script>

View File

@@ -18,12 +18,9 @@
</div>
<v-spacer></v-spacer>
<SearchBar
v-if="!isMobile"
:show-results="true"
@selected="navigateFromSearch"
:max-width="isMobile ? '100%' : '450px'"
/>
<div v-if="!isMobile" style="width: 350px;">
<SearchBar :show-results="true" @selected="navigateFromSearch" :max-width="isMobile ? '100%' : '450px'" />
</div>
<div v-else>
<v-btn icon @click="$refs.recipeSearch.open()">
<v-icon> mdi-magnify </v-icon>

View File

@@ -29,6 +29,14 @@
<!-- Version List Item -->
<v-list nav dense class="fixedBottom" v-if="!isMain">
<v-list-item href="https://github.com/sponsors/hay-kot" target="_target">
<v-list-item-icon >
<v-icon color="pink">
mdi-heart
</v-icon>
</v-list-item-icon>
<v-list-item-title> Support </v-list-item-title>
</v-list-item>
<v-list-item to="/admin/about">
<v-list-item-icon class="mr-3 pt-1">
<v-icon :color="newVersionAvailable ? 'red--text' : ''">