add BackToTopVirtual component

This commit is contained in:
pa
2026-01-25 17:08:25 +09:00
parent ddd191e332
commit 7e312a0e8c
5 changed files with 173 additions and 28 deletions

View File

@@ -95,18 +95,16 @@
</template>
<template #friends>
<div class="h-full overflow-hidden">
<ScrollArea ref="friendsScrollAreaRef" class="h-full">
<ScrollArea class="h-full">
<FriendsSidebar />
</ScrollArea>
<BackToTop :target="friendsScrollTarget" :bottom="20" :right="20" :teleport="false" />
</div>
</template>
<template #groups>
<div class="h-full overflow-hidden">
<ScrollArea ref="groupsScrollAreaRef" class="h-full">
<ScrollArea class="h-full">
<GroupsSidebar />
</ScrollArea>
<BackToTop :target="groupsScrollTarget" :bottom="20" :right="20" :teleport="false" />
</div>
</template>
</TabsUnderline>
@@ -114,8 +112,8 @@
</template>
<script setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { computed, ref, watch } from 'vue';
import { Button } from '@/components/ui/button';
import { DataTableEmpty } from '@/components/ui/data-table';
import { Input } from '@/components/ui/input';
@@ -126,8 +124,6 @@
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import BackToTop from '@/components/BackToTop.vue';
import { useFriendStore, useGroupStore, useSearchStore } from '../../stores';
import { userImage } from '../../shared/utils';
@@ -148,25 +144,6 @@
const quickSearchQuery = ref('');
const isQuickSearchOpen = ref(false);
const friendsScrollAreaRef = ref(null);
const groupsScrollAreaRef = ref(null);
const friendsScrollTarget = ref(null);
const groupsScrollTarget = ref(null);
function resolveScrollViewport(scrollAreaComponentRef) {
// Our ScrollArea renders a DOM element root; the viewport is marked by data-slot.
const rootEl = scrollAreaComponentRef?.$el ?? null;
if (!rootEl || typeof rootEl.querySelector !== 'function') return null;
return rootEl.querySelector('[data-slot="scroll-area-viewport"]');
}
onMounted(async () => {
// Ensure child components are mounted before querying their DOM.
await nextTick();
friendsScrollTarget.value = resolveScrollViewport(friendsScrollAreaRef.value);
groupsScrollTarget.value = resolveScrollViewport(groupsScrollAreaRef.value);
});
watch(
quickSearchQuery,
(value) => {

View File

@@ -79,6 +79,7 @@
</div>
</template>
</div>
<BackToTopVirtual :virtualizer="virtualizer" :target="scrollViewportRef" :teleport-to="scrollRootRef" />
</div>
</template>
@@ -101,6 +102,7 @@
import { isRealInstance, userImage, userStatusClass } from '../../../shared/utils';
import { getFriendsLocations } from '../../../shared/utils/location.js';
import BackToTopVirtual from '../../../components/BackToTopVirtual.vue';
import FriendItem from './FriendItem.vue';
import Location from '../../../components/Location.vue';
import configRepository from '../../../service/config';
@@ -127,6 +129,7 @@
const isSidebarGroupByInstanceCollapsed = ref(false);
const listRootRef = ref(null);
const scrollViewportRef = ref(null);
const scrollRootRef = ref(null);
loadFriendsGroupStates();
@@ -474,6 +477,7 @@
onMounted(() => {
scrollViewportRef.value = listRootRef.value?.closest('[data-slot="scroll-area-viewport"]') ?? null;
scrollRootRef.value = listRootRef.value?.closest('[data-slot="scroll-area"]') ?? null;
nextTick(() => {
virtualizer.value?.measure?.();
});

View File

@@ -45,6 +45,7 @@
</div>
</template>
</div>
<BackToTopVirtual :virtualizer="virtualizer" :target="scrollViewportRef" :teleport-to="scrollRootRef" />
</div>
</template>
@@ -57,6 +58,7 @@
import { useAppearanceSettingsStore, useGroupStore } from '../../../stores';
import { convertFileUrlToImageUrl } from '../../../shared/utils';
import BackToTopVirtual from '../../../components/BackToTopVirtual.vue';
import Location from '../../../components/Location.vue';
const { isAgeGatedInstancesVisible } = storeToRefs(useAppearanceSettingsStore());
@@ -66,6 +68,7 @@
const groupInstancesCfg = ref({});
const listRootRef = ref(null);
const scrollViewportRef = ref(null);
const scrollRootRef = ref(null);
const groupedGroupInstances = computed(() => {
const groupMap = new Map();
@@ -161,7 +164,6 @@
transform: `translateY(${item.virtualItem.start}px)`
});
function getSmallGroupIconUrl(url) {
return convertFileUrlToImageUrl(url);
}
@@ -176,6 +178,7 @@
onMounted(() => {
scrollViewportRef.value = listRootRef.value?.closest('[data-slot="scroll-area-viewport"]') ?? null;
scrollRootRef.value = listRootRef.value?.closest('[data-slot="scroll-area"]') ?? null;
nextTick(() => {
virtualizer.value?.measure?.();
});