Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion unraid-ui/src/components/common/sheet/SheetContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
<template>
<DialogPortal :disabled="disabled" :force-mount="forceMount" :to="teleportTarget">
<DialogOverlay
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/60"
class="data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fixed inset-0 z-50 bg-black/60 data-[state=closed]:duration-200 data-[state=open]:duration-300"
/>
<DialogContent :class="sheetClass" v-bind="forwarded">
<slot />
Expand Down
7 changes: 6 additions & 1 deletion unraid-ui/src/components/common/sheet/sheet.variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';

export const sheetVariants = cva(
'fixed z-50 bg-background gap-4 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 border-border',
// Animate ONLY via the transform/opacity keyframes (animate-in/out). The panel is
// promoted to its own compositor layer (will-change-transform) so the slide runs
// off the main thread and is not stalled by the panel's contents mounting. We do
// NOT add a blanket `transition` here: it would transition every property
// (box-shadow, filter, …) alongside the keyframe and cause per-frame repaints.
'fixed z-50 bg-background gap-4 shadow-lg border-border will-change-transform data-[state=open]:animate-in data-[state=open]:duration-300 data-[state=open]:ease-out data-[state=closed]:animate-out data-[state=closed]:duration-200 data-[state=closed]:ease-in',
{
variants: {
side: {
Expand Down
25 changes: 23 additions & 2 deletions web/src/components/Notifications/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useMutation, useQuery, useSubscription } from '@vue/apollo-composable';

Expand Down Expand Up @@ -139,10 +139,30 @@ const readArchivedCount = computed(() => {
const prepareToViewNotifications = () => {
void recalculateOverview();
};

// Defer mounting the (heavy) tab/list body until the panel has started its open
// animation. The slide is a compositor transform, but the panel's FIRST paint must
// finish before it can show — keeping that first paint cheap (header only) lets the
// slide start immediately instead of hitching while the notification lists mount.
const open = ref(false);
const bodyMounted = ref(false);
watch(open, (isOpen) => {
if (isOpen) {
bodyMounted.value = false;
requestAnimationFrame(() =>
requestAnimationFrame(() => {
if (open.value) bodyMounted.value = true;
})
);
}
// On close, keep the body mounted so it stays visible during the exit slide; reka
// unmounts the whole SheetContent (and the body with it) once the exit animation
// finishes.
});
</script>

<template>
<Sheet>
<Sheet v-model:open="open">
<SheetTrigger as-child>
<Button variant="header" size="header" @click="prepareToViewNotifications">
<span class="sr-only">{{ t('notifications.sidebar.openButtonSr') }}</span>
Expand All @@ -158,6 +178,7 @@ const prepareToViewNotifications = () => {
<SheetTitle class="text-2xl">{{ t('notifications.sidebar.title') }}</SheetTitle>
</SheetHeader>
<Tabs
v-if="bodyMounted"
default-value="unread"
class="flex min-h-0 flex-1 flex-col"
:aria-label="t('notifications.sidebar.statusTabsAria')"
Expand Down
Loading