UI/UX improvements (#2423)

* disable autofocus and hide keyboard on enter

* improve a11y

* fix non-translated string

* improve recipeTimeline UI

* format

* fixes
This commit is contained in:
Arsène Reymond 2023-08-21 20:41:18 +02:00 committed by GitHub
parent 1e693fdca6
commit 99e7717fec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 61 deletions

View File

@ -2,24 +2,24 @@
<v-expand-transition> <v-expand-transition>
<v-card <v-card
:ripple="false" :ripple="false"
class="mx-auto" :class="isFlat ? 'mx-auto flat' : 'mx-auto'"
hover hover
:to="$listeners.selected ? undefined : `/recipe/${slug}`" :to="$listeners.selected ? undefined : `/recipe/${slug}`"
@click="$emit('selected')" @click="$emit('selected')"
> >
<v-img v-if="vertical"> <v-img v-if="vertical" class="rounded-sm">
<RecipeCardImage <RecipeCardImage
:icon-size="100" :icon-size="100"
:height="75" :height="150"
:slug="slug" :slug="slug"
:recipe-id="recipeId" :recipe-id="recipeId"
small small
:image-version="image" :image-version="image"
/> />
</v-img> </v-img>
<v-list-item three-line> <v-list-item three-line class="px-0">
<slot v-if="!vertical" name="avatar"> <slot v-if="!vertical" name="avatar">
<v-list-item-avatar tile size="125" class="v-mobile-img rounded-sm my-0 ml-n4"> <v-list-item-avatar tile size="125" class="v-mobile-img rounded-sm my-0">
<RecipeCardImage <RecipeCardImage
:icon-size="100" :icon-size="100"
:height="125" :height="125"
@ -30,8 +30,8 @@
/> />
</v-list-item-avatar> </v-list-item-avatar>
</slot> </slot>
<v-list-item-content> <v-list-item-content class="py-0">
<v-list-item-title class="mb-1">{{ name }} </v-list-item-title> <v-list-item-title class="mt-3 mb-1">{{ name }} </v-list-item-title>
<v-list-item-subtitle> <v-list-item-subtitle>
<SafeMarkdown :source="description" /> <SafeMarkdown :source="description" />
</v-list-item-subtitle> </v-list-item-subtitle>
@ -120,7 +120,11 @@ export default defineComponent({
vertical: { vertical: {
type: Boolean, type: Boolean,
default: false, default: false,
} },
isFlat: {
type: Boolean,
default: false,
},
}, },
setup() { setup() {
const { $auth } = useContext(); const { $auth } = useContext();
@ -162,4 +166,9 @@ export default defineComponent({
.text-top { .text-top {
align-self: start !important; align-self: start !important;
} }
.flat {
box-shadow: none!important;
background-color: transparent;
}
</style> </style>

View File

@ -149,7 +149,7 @@
:nudge-top="menuTop ? '5' : '0'" :nudge-top="menuTop ? '5' : '0'"
allow-overflow allow-overflow
close-delay="125" close-delay="125"
open-on-hover :open-on-hover="$vuetify.breakpoint.mdAndUp"
content-class="d-print-none" content-class="d-print-none"
> >
<template #activator="{ on, attrs }"> <template #activator="{ on, attrs }">

View File

@ -62,7 +62,7 @@
class="d-print-none d-flex px-2" class="d-print-none d-flex px-2"
:class="$vuetify.breakpoint.smAndDown ? 'justify-center' : 'justify-end'" :class="$vuetify.breakpoint.smAndDown ? 'justify-center' : 'justify-end'"
> >
<v-switch v-model="wakeLock" small label="Keep Screen Awake" /> <v-switch v-model="wakeLock" small :label="$t('recipe.screen-awake')" />
</div> </div>
<RecipePageComments <RecipePageComments

View File

@ -8,8 +8,7 @@
</v-btn> </v-btn>
</v-col> </v-col>
</v-row> </v-row>
<v-divider v-if="timelineEvents.length" /> <div
<v-card
v-if="timelineEvents.length" v-if="timelineEvents.length"
id="timeline-container" id="timeline-container"
height="fit-content" height="fit-content"
@ -27,13 +26,13 @@
@delete="deleteTimelineEvent(index)" @delete="deleteTimelineEvent(index)"
/> />
</v-timeline> </v-timeline>
</v-card> </div>
<v-card v-else-if="!loading"> <v-card v-else-if="!loading">
<v-card-title class="justify-center pa-9"> <v-card-title class="justify-center pa-9">
{{ $t("recipe.timeline-is-empty") }} {{ $t("recipe.timeline-is-empty") }}
</v-card-title> </v-card-title>
</v-card> </v-card>
<div v-if="loading" class="pb-3"> <div v-if="loading" class="mb-3">
<AppLoader :loading="loading" :waiting-text="$tc('general.loading-events')" /> <AppLoader :loading="loading" :waiting-text="$tc('general.loading-events')" />
</div> </div>
</div> </div>

View File

@ -41,7 +41,7 @@
:nudge-top="menuTop ? '5' : '0'" :nudge-top="menuTop ? '5' : '0'"
allow-overflow allow-overflow
close-delay="125" close-delay="125"
open-on-hover :open-on-hover="!useMobileFormat"
content-class="d-print-none" content-class="d-print-none"
> >
<template #activator="{ on, attrs }"> <template #activator="{ on, attrs }">
@ -121,6 +121,10 @@ export default defineComponent({
type: String, type: String,
default: null, default: null,
}, },
useMobileFormat: {
type: Boolean,
default: true,
}
}, },
setup(props, context) { setup(props, context) {
const domEditEventForm = ref<VForm>(); const domEditEventForm = ref<VForm>();

View File

@ -11,9 +11,12 @@
{{ new Date(event.timestamp+"Z").toLocaleDateString($i18n.locale) }} {{ new Date(event.timestamp+"Z").toLocaleDateString($i18n.locale) }}
</v-chip> </v-chip>
</template> </template>
<v-card> <v-card
hover
:to="$listeners.selected ? undefined : `/recipe/${recipe.slug}`"
@click="$emit('selected')">
<v-sheet> <v-sheet>
<v-card-title> <v-card-title class="bg-primary">
<v-row> <v-row>
<v-col align-self="center" :cols="useMobileFormat ? 'auto' : '2'" :class="attrs.avatar.class"> <v-col align-self="center" :cols="useMobileFormat ? 'auto' : '2'" :class="attrs.avatar.class">
<UserAvatar :user-id="event.userId" :size="attrs.avatar.size" /> <UserAvatar :user-id="event.userId" :size="attrs.avatar.size" />
@ -34,6 +37,7 @@
:menu-top="false" :menu-top="false"
:event="event" :event="event"
:menu-icon="$globals.icons.dotsVertical" :menu-icon="$globals.icons.dotsVertical"
:use-mobile-format="useMobileFormat"
fab fab
color="transparent" color="transparent"
:elevation="0" :elevation="0"
@ -48,8 +52,8 @@
</v-col> </v-col>
</v-row> </v-row>
</v-card-title> </v-card-title>
<v-sheet v-if="showRecipeCards && recipe"> <v-card-text v-if="showRecipeCards && recipe">
<v-row class="pt-3 pb-7 mx-3" style="max-width: 100%;"> <v-row :class="useMobileFormat ? 'py-3 mx-0' : 'py-3 mx-0'" style="max-width: 100%;">
<v-col align-self="center" class="pa-0"> <v-col align-self="center" class="pa-0">
<RecipeCardMobile <RecipeCardMobile
:vertical="useMobileFormat" :vertical="useMobileFormat"
@ -59,12 +63,14 @@
:rating="recipe.rating" :rating="recipe.rating"
:image="recipe.image" :image="recipe.image"
:recipe-id="recipe.id" :recipe-id="recipe.id"
:is-flat="true"
/> />
</v-col> </v-col>
</v-row> </v-row>
</v-card-text>
</v-sheet> </v-sheet>
<v-divider v-if="showRecipeCards && recipe && (useMobileFormat || event.eventMessage || (eventImageUrl && !hideImage))" /> <v-divider v-if="showRecipeCards && recipe && (useMobileFormat || event.eventMessage)" />
<v-card-text> <v-card-text v-if="showRecipeCards && recipe && (useMobileFormat || event.eventMessage)">
<v-row> <v-row>
<v-col> <v-col>
<strong v-if="useMobileFormat">{{ event.subject }}</strong> <strong v-if="useMobileFormat">{{ event.subject }}</strong>
@ -84,7 +90,6 @@
</v-col> </v-col>
</v-row> </v-row>
</v-card-text> </v-card-text>
</v-sheet>
</v-card> </v-card>
</v-timeline-item> </v-timeline-item>
</template> </template>
@ -191,3 +196,9 @@ export default defineComponent({
}, },
}); });
</script> </script>
<style>
.v-card::after {
display: none;
}
</style>

View File

@ -26,7 +26,7 @@
dense dense
flat flat
:prepend-inner-icon="$globals.icons.search" :prepend-inner-icon="$globals.icons.search"
background-color="primary lighten-1" background-color="primary darken-1"
color="white" color="white"
:placeholder="$t('search.search-hint')" :placeholder="$t('search.search-hint')"
> >
@ -35,7 +35,7 @@
<v-btn v-else icon @click="activateSearch"> <v-btn v-else icon @click="activateSearch">
<v-icon> {{ $globals.icons.search }}</v-icon> <v-icon> {{ $globals.icons.search }}</v-icon>
</v-btn> </v-btn>
<v-btn v-if="$auth.loggedIn" text @click="$auth.logout()"> <v-btn v-if="$auth.loggedIn" :text="$vuetify.breakpoint.smAndUp" :icon="$vuetify.breakpoint.xs" @click="$auth.logout()">
<v-icon :left="$vuetify.breakpoint.smAndUp">{{ $globals.icons.logout }}</v-icon> <v-icon :left="$vuetify.breakpoint.smAndUp">{{ $globals.icons.logout }}</v-icon>
{{ $vuetify.breakpoint.smAndUp ? $t("user.logout") : "" }} {{ $vuetify.breakpoint.smAndUp ? $t("user.logout") : "" }}
</v-btn> </v-btn>

View File

@ -531,6 +531,7 @@
"recipe-yield": "Recipe Yield", "recipe-yield": "Recipe Yield",
"unit": "Unit", "unit": "Unit",
"upload-image": "Upload image", "upload-image": "Upload image",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image" "remove-image": "Remove image"
}, },
"search": { "search": {

View File

@ -4,14 +4,15 @@
<form class="search-box pa-2" @submit.prevent="search"> <form class="search-box pa-2" @submit.prevent="search">
<div class="d-flex justify-center my-2"> <div class="d-flex justify-center my-2">
<v-text-field <v-text-field
ref="input"
v-model="state.search" v-model="state.search"
outlined outlined
autofocus
hide-details hide-details
clearable clearable
color="primary" color="primary"
:placeholder="$tc('search.search-placeholder')" :placeholder="$tc('search.search-placeholder')"
:prepend-inner-icon="$globals.icons.search" :prepend-inner-icon="$globals.icons.search"
@keyup.enter="hideKeyboard"
/> />
</div> </div>
<div class="search-row"> <div class="search-row">
@ -132,7 +133,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, defineComponent, useRouter, onMounted, useContext, computed } from "@nuxtjs/composition-api"; import { ref, defineComponent, useRouter, onMounted, useContext, computed, Ref } from "@nuxtjs/composition-api";
import { watchDebounced } from "@vueuse/shared"; import { watchDebounced } from "@vueuse/shared";
import SearchFilter from "~/components/Domain/SearchFilter.vue"; import SearchFilter from "~/components/Domain/SearchFilter.vue";
import { useCategoryStore, useFoodStore, useTagStore, useToolStore } from "~/composables/store"; import { useCategoryStore, useFoodStore, useTagStore, useToolStore } from "~/composables/store";
@ -205,6 +206,12 @@ export default defineComponent({
return array.map((item) => item.id); return array.map((item) => item.id);
} }
function hideKeyboard() {
input.value.blur()
}
const input: Ref<any> = ref(null);
async function search() { async function search() {
await router.push({ await router.push({
query: { query: {
@ -432,6 +439,8 @@ export default defineComponent({
sortable, sortable,
toggleOrderDirection, toggleOrderDirection,
hideKeyboard,
input,
selectedCategories, selectedCategories,
selectedFoods, selectedFoods,

View File

@ -2,7 +2,7 @@
<v-container <v-container
fill-height fill-height
fluid fluid
class="d-flex justify-center align-center" class="d-flex justify-center align-center flex-column"
:class="{ :class="{
'bg-off-white': !$vuetify.theme.dark && !isDark, 'bg-off-white': !$vuetify.theme.dark && !isDark,
}" }"
@ -23,7 +23,7 @@
</v-avatar> </v-avatar>
</div> </div>
<v-card-title class="headline justify-center pb-1"> {{ $t('user.sign-in') }} </v-card-title> <v-card-title class="headline justify-center pb-3"> {{ $t('user.sign-in') }} </v-card-title>
<v-card-text> <v-card-text>
<v-form @submit.prevent="authenticate"> <v-form @submit.prevent="authenticate">
<v-text-field <v-text-field
@ -68,7 +68,7 @@
<v-divider></v-divider> <v-divider></v-divider>
<v-card-text class="d-flex justify-center"> <v-card-text class="d-flex justify-center flex-column flex-sm-row">
<div <div
v-for="link in [ v-for="link in [
{ {
@ -88,6 +88,7 @@
}, },
]" ]"
:key="link.text" :key="link.text"
class="text-center"
> >
<v-btn text :href="link.href" target="_blank"> <v-btn text :href="link.href" target="_blank">
<v-icon left> <v-icon left>
@ -99,7 +100,7 @@
</v-card-text> </v-card-text>
</v-card> </v-card>
<v-btn absolute bottom center @click="toggleDark"> <v-btn bottom center class="mt-5" @click="toggleDark">
<v-icon left> <v-icon left>
{{ $vuetify.theme.dark ? $globals.icons.weatherSunny : $globals.icons.weatherNight }} {{ $vuetify.theme.dark ? $globals.icons.weatherSunny : $globals.icons.weatherNight }}
</v-icon> </v-icon>