refactor: remove useDataTableScrollHeight and introducing an auto-height prop and CSS class.

This commit is contained in:
pa
2026-03-18 09:31:30 +09:00
parent b33821ba82
commit ab596a13b9
18 changed files with 32 additions and 225 deletions

View File

@@ -1,11 +1,11 @@
<template>
<div class="flex flex-col min-w-0 data-table">
<div :class="['flex flex-col min-w-0 data-table', autoHeight && 'flex-1 min-h-0 overflow-hidden']">
<div v-if="$slots.toolbar" class="mb-2">
<slot name="toolbar"></slot>
</div>
<div class="rounded-md border">
<div ref="tableScrollRef" class="max-w-full overflow-auto relative" :style="tableStyle">
<div :class="['rounded-md border', autoHeight && 'flex-1 min-h-0 flex flex-col overflow-hidden']">
<div ref="tableScrollRef" :class="['max-w-full overflow-auto relative', autoHeight && 'flex-1 min-h-0']" :style="tableStyle">
<Table :class="tableClassValue" :style="tableElementStyle">
<colgroup>
<col v-for="col in table.getVisibleLeafColumns()" :key="col.id" :style="getColStyle(col)" />
@@ -259,7 +259,7 @@
</div>
</div>
<div v-if="showPagination" class="mt-4 flex w-full items-center gap-3">
<div v-if="showPagination" class="mt-4 flex w-full items-center gap-3 mb-1">
<div v-if="pageSizes.length" class="inline-flex items-center flex-1 justify-end gap-2">
<span class="text-xs text-muted-foreground truncate">{{ t('table.pagination.rows_per_page') }}</span>
<Select v-model="pageSizeValue">
@@ -399,6 +399,10 @@
enableColumnVisibility: {
type: Boolean,
default: true
},
autoHeight: {
type: Boolean,
default: false
}
});

View File

@@ -1,114 +0,0 @@
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
export function useDataTableScrollHeight(containerRef, options = {}) {
const {
offset = 127,
toolbarHeight = 0,
paginationHeight = 0,
extraOffsetRefs = [],
subtractContainerPadding = false
} = options;
const maxHeight = ref(0);
let resizeObserver;
const observedElements = new Set();
const getPadding = (el) => {
if (!subtractContainerPadding || !el) {
return 0;
}
const style = getComputedStyle(el);
return (
(Number.parseFloat(style.paddingTop) || 0) +
(Number.parseFloat(style.paddingBottom) || 0)
);
};
const getHeight = (maybeRef) => {
const el = maybeRef?.value;
return el && typeof el.getBoundingClientRect === 'function'
? el.getBoundingClientRect().height
: 0;
};
const recalc = () => {
const containerEl = containerRef?.value;
if (!containerEl) {
return;
}
const extraOffset = extraOffsetRefs.reduce(
(sum, ref) => sum + getHeight(ref),
0
);
const available =
containerEl.clientHeight -
getPadding(containerEl) -
offset -
toolbarHeight -
paginationHeight -
extraOffset;
maxHeight.value = Math.max(0, available);
};
const updateObservedElements = () => {
if (!resizeObserver) {
return;
}
const nextObserved = new Set(
[
containerRef?.value,
...extraOffsetRefs.map((ref) => ref?.value)
].filter(Boolean)
);
for (const el of observedElements) {
if (!nextObserved.has(el)) {
resizeObserver.unobserve(el);
observedElements.delete(el);
}
}
for (const el of nextObserved) {
if (!observedElements.has(el)) {
resizeObserver.observe(el);
observedElements.add(el);
}
}
};
onMounted(() => {
resizeObserver = new ResizeObserver(recalc);
updateObservedElements();
recalc();
});
watch(
() => [containerRef?.value, ...extraOffsetRefs.map((r) => r?.value)],
() => {
updateObservedElements();
recalc();
},
{ flush: 'post' }
);
onUnmounted(() => {
resizeObserver?.disconnect();
observedElements.clear();
});
const tableStyle = computed(() => {
if (!Number.isFinite(maxHeight.value) || maxHeight.value <= 0)
return undefined;
return { maxHeight: `${maxHeight.value}px` };
});
return {
tableStyle
};
}

View File

@@ -197,6 +197,12 @@ html {
border: 1px solid var(--border);
}
.x-container--auto-height {
display: flex;
flex-direction: column;
overflow: hidden;
}
.aside-collapsed .x-container {
margin-right: 8px;
}

View File

@@ -1,9 +1,9 @@
<template>
<div class="x-container feed" ref="feedRef">
<div class="x-container feed x-container--auto-height" ref="feedRef">
<DataTableLayout
:table="table"
:loading="feedTable.loading"
:table-style="tableHeightStyle"
auto-height
:page-sizes="pageSizes"
:total-items="totalItems"
:on-page-size-change="handlePageSizeChange">
@@ -86,7 +86,7 @@
</template>
<script setup>
import { computed, ref, watch } from 'vue';
import { computed, ref } from 'vue';
import { ListFilter, Star } from 'lucide-vue-next';
import { getLocalTimeZone, today } from '@internationalized/date';
import { storeToRefs } from 'pinia';
@@ -104,7 +104,6 @@
import { RangeCalendar } from '../../components/ui/range-calendar';
import { Toggle } from '../../components/ui/toggle';
import { columns as baseColumns } from './columns.jsx';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
const { feedTable, feedTableData } = storeToRefs(useFeedStore());
@@ -154,13 +153,6 @@
const feedRef = ref(null);
// TODO: simplify
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(feedRef, {
offset: 30,
toolbarHeight: 54,
paginationHeight: 52
});
const pageSizes = computed(() => appearanceSettingsStore.tablePageSizes);
/**

View File

@@ -67,12 +67,6 @@ vi.mock('../../../lib/table/useVrcxVueTable', () => ({
})
}));
vi.mock('../../../composables/useDataTableScrollHeight', () => ({
useDataTableScrollHeight: () => ({
tableStyle: {}
})
}));
vi.mock('../columns.jsx', () => ({
columns: []
}));

View File

@@ -1,11 +1,11 @@
<template>
<div class="x-container" ref="friendsListRef">
<div>
<div class="x-container x-container--auto-height" ref="friendsListRef">
<div class="flex-1 min-h-0 flex flex-col">
<DataTableLayout
class="min-w-0 w-full"
:table="table"
:loading="friendsListLoading"
:table-style="tableHeightStyle"
auto-height
:page-sizes="pageSizes"
:total-items="totalItems"
table-class="min-w-max w-max [&_tbody_tr]:cursor-pointer"
@@ -144,7 +144,6 @@
import { localeIncludes } from '../../shared/utils';
import removeConfusables, { removeWhitespace } from '../../services/confusables';
import { router } from '../../plugins/router';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
import { showUserDialog } from '../../coordinators/userCoordinator';
import { confirmDeleteFriend, handleFriendDelete } from '../../coordinators/friendRelationshipCoordinator';
@@ -184,11 +183,6 @@
});
const friendsListRef = ref(null);
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(friendsListRef, {
offset: 30,
toolbarHeight: 54,
paginationHeight: 52
});
const friendsListColumns = computed(() =>
createColumns({

View File

@@ -127,12 +127,6 @@ vi.mock('../../../shared/utils', () => ({
.includes(String(query ?? '').toLowerCase())
}));
vi.mock('../../../composables/useDataTableScrollHeight', () => ({
useDataTableScrollHeight: () => ({
tableStyle: {}
})
}));
vi.mock('../../../lib/table/useVrcxVueTable', () => ({
useVrcxVueTable: (options) => ({
table: {

View File

@@ -1,9 +1,9 @@
<template>
<div class="x-container" ref="friendLogRef">
<div class="x-container x-container--auto-height" ref="friendLogRef">
<DataTableLayout
:table="table"
:loading="friendLogTable.loading"
:table-style="tableHeightStyle"
auto-height
:page-sizes="pageSizes"
:total-items="totalItems"
:on-page-size-change="handlePageSizeChange">
@@ -61,7 +61,6 @@
import { createColumns } from './columns.jsx';
import { database } from '../../services/database';
import { removeFromArray } from '../../shared/utils';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
import configRepository from '../../services/config';
@@ -73,11 +72,6 @@
const { friendLogTable } = storeToRefs(useFriendStore());
const friendLogRef = ref(null);
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(friendLogRef, {
offset: 30,
toolbarHeight: 54,
paginationHeight: 52
});
const friendLogDisplayData = computed(() => {
const data = friendLogTable.value.data;

View File

@@ -55,12 +55,6 @@ vi.mock('../../../stores', () => ({
})
}));
vi.mock('../../../composables/useDataTableScrollHeight', () => ({
useDataTableScrollHeight: () => ({
tableStyle: {}
})
}));
vi.mock('../../../lib/table/useVrcxVueTable', () => ({
useVrcxVueTable: (options) => ({
table: {

View File

@@ -1,9 +1,9 @@
<template>
<div class="x-container" ref="gameLogRef">
<div class="x-container x-container--auto-height" ref="gameLogRef">
<DataTableLayout
:table="table"
:loading="gameLogTable.loading"
:table-style="tableHeightStyle"
auto-height
:page-sizes="pageSizes"
:total-items="totalItems"
:on-page-size-change="handlePageSizeChange">
@@ -82,7 +82,6 @@
import { createColumns } from './columns.jsx';
import { database } from '../../services/database';
import { removeFromArray } from '../../shared/utils';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
const { gameLogTableLookup } = useGameLogStore();
@@ -111,11 +110,6 @@
const { t } = useI18n();
const gameLogRef = ref(null);
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(gameLogRef, {
offset: 30,
toolbarHeight: 54,
paginationHeight: 52
});
/**
*

View File

@@ -48,9 +48,6 @@ vi.mock('../../../services/database', () => ({
database: { deleteGameLogEntry: vi.fn() }
}));
vi.mock('../../../shared/utils', () => ({ removeFromArray: vi.fn() }));
vi.mock('../../../composables/useDataTableScrollHeight', () => ({
useDataTableScrollHeight: () => ({ tableStyle: ref({}) })
}));
vi.mock('../../../lib/table/useVrcxVueTable', () => ({
useVrcxVueTable: () => ({
table: { getFilteredRowModel: () => ({ rows: [] }) },

View File

@@ -1,5 +1,5 @@
<template>
<div class="x-container" ref="moderationRef">
<div class="x-container x-container--auto-height" ref="moderationRef">
<div class="mb-4 flex items-center">
<Select
multiple
@@ -40,7 +40,7 @@
<DataTableLayout
:table="table"
:loading="playerModerationTable.loading"
:table-style="tableHeightStyle"
auto-height
:page-sizes="pageSizes"
:total-items="totalItems"
:on-page-size-change="handlePageSizeChange" />
@@ -63,7 +63,6 @@
import { createColumns } from './columns.jsx';
import { moderationTypes } from '../../shared/constants';
import { playerModerationRequest } from '../../api';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
import configRepository from '../../services/config.js';
@@ -76,11 +75,6 @@
const modalStore = useModalStore();
const moderationRef = ref(null);
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(moderationRef, {
offset: 30,
toolbarHeight: 54,
paginationHeight: 52
});
async function init() {
playerModerationTable.value.filters[0].value = JSON.parse(

View File

@@ -103,12 +103,6 @@ vi.mock('../../../lib/table/useVrcxVueTable', () => ({
})
}));
vi.mock('../../../composables/useDataTableScrollHeight', () => ({
useDataTableScrollHeight: () => ({
tableStyle: {}
})
}));
vi.mock('@/components/ui/select', () => ({
Select: {
template: '<div><slot /></div>'

View File

@@ -167,7 +167,7 @@
<DataTableLayout
v-if="viewMode === 'table'"
:table="table"
:table-style="tableHeightStyle"
auto-height
:page-sizes="pageSizes"
:total-items="filteredAvatars.length"
:loading="isLoading"
@@ -340,7 +340,6 @@
import { getTagColor } from '../../shared/constants';
import { processBulk } from '../../services/request';
import { useAvatarCardGrid } from './composables/useAvatarCardGrid';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
import ImageCropDialog from '../../components/dialogs/ImageCropDialog.vue';
@@ -375,11 +374,7 @@
const manageTagsOpen = ref(false);
const manageTagsAvatar = ref(null);
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(containerRef, {
offset: 30,
toolbarHeight: 54,
paginationHeight: 52
});
const allTags = computed(() => {
const tagSet = new Set();

View File

@@ -154,12 +154,6 @@ vi.mock('../composables/useAvatarCardGrid.js', () => ({
})
}));
vi.mock('../../../composables/useDataTableScrollHeight', () => ({
useDataTableScrollHeight: () => ({
tableStyle: {}
})
}));
vi.mock('../../../lib/table/useVrcxVueTable', () => ({
useVrcxVueTable: () => ({
table: {},

View File

@@ -1,9 +1,9 @@
<template>
<div class="x-container" ref="notificationsRef">
<div class="x-container x-container--auto-height" ref="notificationsRef">
<DataTableLayout
:table="table"
:loading="isNotificationsLoading"
:table-style="tableHeightStyle"
auto-height
:page-sizes="pageSizes"
:total-items="totalItems"
:on-page-size-change="handlePageSizeChange">
@@ -104,7 +104,6 @@
import { DataTableLayout } from '../../components/ui/data-table';
import { convertFileUrlToImageUrl } from '../../shared/utils';
import { createColumns } from './columns.jsx';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
import SendInviteRequestResponseDialog from './dialogs/SendInviteRequestResponseDialog.vue';
@@ -132,11 +131,6 @@
const { t } = useI18n();
const notificationsRef = ref(null);
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(notificationsRef, {
offset: 30,
toolbarHeight: 54,
paginationHeight: 52
});
/**
*

View File

@@ -1,5 +1,5 @@
<template>
<div class="x-container" ref="playerListRef">
<div class="x-container x-container--auto-height" ref="playerListRef">
<div class="flex h-full min-h-0 flex-col overflow-y-auto overflow-x-hidden">
<div
v-if="currentInstanceWorld.ref.id"
@@ -168,7 +168,7 @@
<DataTableLayout
class="[&_th]:px-2.5! [&_th]:py-0.75! [&_td]:px-2.5! [&_td]:py-0.75! [&_tr]:h-7!"
:table="playerListTable"
:table-style="playerListTableStyle"
auto-height
:loading="false"
:show-pagination="false"
:on-row-click="handlePlayerListRowClick" />
@@ -198,7 +198,6 @@
import { Badge } from '../../components/ui/badge';
import { DataTableLayout } from '../../components/ui/data-table';
import { createColumns } from './columns.jsx';
import { useDataTableScrollHeight } from '../../composables/useDataTableScrollHeight';
import { useVrcxVueTable } from '../../lib/table/useVrcxVueTable';
import ChatboxBlacklistDialog from './dialogs/ChatboxBlacklistDialog.vue';
@@ -231,12 +230,6 @@
const playerListRef = ref(null);
const playerListHeaderRef = ref(null);
const playerListPhotonRef = ref(null);
const { tableStyle: playerListTableStyle } = useDataTableScrollHeight(playerListRef, {
offset: 30,
paginationHeight: 0,
subtractContainerPadding: true,
extraOffsetRefs: [playerListHeaderRef, playerListPhotonRef]
});
const { t } = useI18n();

View File

@@ -108,12 +108,6 @@ vi.mock('../../../lib/table/useVrcxVueTable', () => ({
})
}));
vi.mock('../../../composables/useDataTableScrollHeight', () => ({
useDataTableScrollHeight: () => ({
tableStyle: {}
})
}));
vi.mock('../columns.jsx', () => ({
createColumns: () => [{ id: 'photonId' }]
}));