mirror of
https://github.com/jellyfin/jellyfin-vue.git
synced 2026-09-03 05:10:26 +03:00
fix: transitions
Use Vuetify transition classes and add new `enter` and `leave` hooks for further transition customization.
This commit is contained in:
@@ -1,61 +0,0 @@
|
||||
/* Default transitions */
|
||||
|
||||
.page-enter-active,
|
||||
.layout-enter-active {
|
||||
animation: enter-page 0.2s;
|
||||
}
|
||||
|
||||
.page-leave-active,
|
||||
.layout-leave-active {
|
||||
animation: leave-page 0.2s;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
.page-enter-active,
|
||||
.layout-enter-active {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes enter-page {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(10px, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: initial;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leave-page {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: initial;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate3d(-10px, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Slide-up transition */
|
||||
.slideUp-enter-active,
|
||||
.slideUp-leave-active {
|
||||
animation: slideUp-enter 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slideUp-enter {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: initial;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,28 @@
|
||||
<template>
|
||||
<router-view v-if="enableTransitions" v-slot="{ Component, route }">
|
||||
<transition :name="route.meta.transition || 'page'" mode="out-in">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition
|
||||
:name="!prefersNoMotion ? getTransitionName(route) : undefined"
|
||||
mode="out-in"
|
||||
@before-enter="(el: HTMLElement) => el.style.transformOrigin = 'center center'">
|
||||
<!-- This div is required because <transition> requires a single children node -->
|
||||
<div
|
||||
:key="getKey(route)"
|
||||
class="h-100"
|
||||
:class="
|
||||
isRoot
|
||||
? 'router-view-root-transition-wrapper'
|
||||
: 'router-view-transition-wrapper'
|
||||
">
|
||||
<div :key="isRoot ? route.meta.layout : String(route.name)" class="h-100">
|
||||
<Suspense>
|
||||
<component :is="Component" />
|
||||
</Suspense>
|
||||
</div>
|
||||
</transition>
|
||||
</router-view>
|
||||
|
||||
<router-view v-else v-slot="{ Component }">
|
||||
<Suspense>
|
||||
<component :is="Component" />
|
||||
</Suspense>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouteLocationNormalized } from 'vue-router';
|
||||
<script lang="ts">
|
||||
import { RouteLocationNormalizedLoaded } from 'vue-router';
|
||||
import { useMediaQuery } from '@vueuse/core';
|
||||
|
||||
interface Props {
|
||||
const prefersNoMotion = useMediaQuery('(prefers-reduced-motion)');
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
/**
|
||||
* Root transitions happen between layout changes. e.g. from `default` layout to `main` and viceversa.
|
||||
* This should be "true" for the App.vue component
|
||||
@@ -35,22 +30,18 @@ interface Props {
|
||||
* layout
|
||||
*/
|
||||
isRoot?: boolean;
|
||||
enableTransitions?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isRoot: false,
|
||||
enableTransitions: true
|
||||
});
|
||||
}>();
|
||||
|
||||
/**
|
||||
* Gets the key to re-render the component tree of the current page
|
||||
* Get transition name
|
||||
*/
|
||||
function getKey(route: RouteLocationNormalized): string {
|
||||
if (props.isRoot) {
|
||||
return route.meta.layout;
|
||||
function getTransitionName(
|
||||
route: RouteLocationNormalizedLoaded
|
||||
): undefined | string {
|
||||
if (route.meta.transition?.enter) {
|
||||
return route.meta.transition.enter;
|
||||
}
|
||||
|
||||
return String(route.name);
|
||||
return 'scroll-x-reverse-transition';
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,6 @@ import { hideDirective } from '@/plugins/directives';
|
||||
* - GLOBAL STYLES -
|
||||
*/
|
||||
import '@/assets/styles/global.scss';
|
||||
import '@/assets/styles/transitions.scss';
|
||||
|
||||
/**
|
||||
* - VUE PLUGINS, STORE AND DIRECTIVE -
|
||||
|
||||
@@ -170,10 +170,6 @@ const homeSections = computed<HomeSection[]>(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-carousel {
|
||||
margin-top: -64px;
|
||||
}
|
||||
|
||||
.sections-after-header {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
|
||||
@@ -70,7 +70,9 @@ meta:
|
||||
layout: fullpage
|
||||
backdrop:
|
||||
opacity: 0.75
|
||||
transition: slideUp
|
||||
transition:
|
||||
enter: 'scroll-y-reverse-transition'
|
||||
leave: 'scroll-y-transition'
|
||||
</route>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -112,7 +112,9 @@
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: fullpage
|
||||
transition: slideUp
|
||||
transition:
|
||||
enter: 'scroll-y-reverse-transition'
|
||||
leave: 'scroll-y-transition'
|
||||
</route>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -15,26 +15,22 @@ const routes = new Set([serverAddUrl, serverSelectUrl, serverLoginUrl]);
|
||||
* Redirects to login page if there's no user logged in.
|
||||
*/
|
||||
export default function loginGuard(
|
||||
to?: RouteLocationNormalized
|
||||
to: RouteLocationNormalized
|
||||
): boolean | RouteLocationRaw {
|
||||
const remote = useRemote();
|
||||
let destinationRoute: RouteLocationPathRaw | undefined;
|
||||
|
||||
if (to) {
|
||||
if (remote.auth.servers.length <= 0) {
|
||||
destinationRoute = { path: serverAddUrl, replace: true };
|
||||
} else if (!routes.has(to.path)) {
|
||||
if (isNil(remote.auth.currentServer)) {
|
||||
destinationRoute = { path: serverSelectUrl, replace: true };
|
||||
} else if (isNil(remote.auth.currentUser)) {
|
||||
destinationRoute = { path: serverLoginUrl, replace: true };
|
||||
}
|
||||
if (remote.auth.servers.length <= 0) {
|
||||
destinationRoute = { path: serverAddUrl, replace: true };
|
||||
} else if (!routes.has(to.path)) {
|
||||
if (isNil(remote.auth.currentServer)) {
|
||||
destinationRoute = { path: serverSelectUrl, replace: true };
|
||||
} else if (isNil(remote.auth.currentUser)) {
|
||||
destinationRoute = { path: serverLoginUrl, replace: true };
|
||||
}
|
||||
|
||||
return destinationRoute && to.path !== destinationRoute.path
|
||||
? destinationRoute
|
||||
: true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return destinationRoute && to.path !== destinationRoute.path
|
||||
? destinationRoute
|
||||
: true;
|
||||
}
|
||||
|
||||
@@ -38,9 +38,18 @@ const defaultMeta: RouteMeta = {
|
||||
* present at the plugins.d.ts file
|
||||
*/
|
||||
export default function metaGuard(
|
||||
to: RouteLocationNormalized
|
||||
to: RouteLocationNormalized,
|
||||
from: RouteLocationNormalized
|
||||
): boolean | RouteLocationRaw {
|
||||
to.meta = reactive<RouteMeta>(defaultsDeep(to.meta, defaultMeta));
|
||||
|
||||
if (from.meta.transition?.leave) {
|
||||
if (!to.meta.transition) {
|
||||
to.meta.transition = { enter: from.meta.transition.leave };
|
||||
} else {
|
||||
to.meta.transition.enter = from.meta.transition.leave;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Vendored
+5
-1
@@ -12,11 +12,15 @@ interface BackdropPayload {
|
||||
blurhash?: string;
|
||||
opacity?: number;
|
||||
}
|
||||
interface RouteTransition {
|
||||
enter: string;
|
||||
leave?: string;
|
||||
}
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
readonly layout: string;
|
||||
transparentLayout?: boolean;
|
||||
transition?: string;
|
||||
transition?: RouteTransition;
|
||||
readonly admin: boolean;
|
||||
title?: string | null;
|
||||
backdrop: BackdropPayload;
|
||||
|
||||
Reference in New Issue
Block a user