From 0f153682bf6ef79a3f4085a6be3730ec48adb26f Mon Sep 17 00:00:00 2001 From: tesaide Date: Thu, 4 Dec 2025 13:34:26 +0200 Subject: [PATCH 1/3] fix: prevent skeleton layout shift during pagination --- adminforth/spa/src/components/ResourceListTable.vue | 8 ++++++-- .../spa/src/components/ResourceListTableVirtual.vue | 12 ++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/adminforth/spa/src/components/ResourceListTable.vue b/adminforth/spa/src/components/ResourceListTable.vue index d940068b..ea9bfab2 100644 --- a/adminforth/spa/src/components/ResourceListTable.vue +++ b/adminforth/spa/src/components/ResourceListTable.vue @@ -418,8 +418,12 @@ const rowHeights = ref([]); const columnWidths = ref([]); watch(() => props.rows, (newRows) => { // rows are set to null when new records are loading - rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight); - columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)]; +if (!newRows) return; + + nextTick(() => { + if (rowRefs.value) rowHeights.value = rowRefs.value.map((el: HTMLElement) => el.offsetHeight); + if (headerRefs.value) columnWidths.value = [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)]; + }); }); function addToCheckedValues(id: string) { diff --git a/adminforth/spa/src/components/ResourceListTableVirtual.vue b/adminforth/spa/src/components/ResourceListTableVirtual.vue index 900ecba9..cae28a62 100644 --- a/adminforth/spa/src/components/ResourceListTableVirtual.vue +++ b/adminforth/spa/src/components/ResourceListTableVirtual.vue @@ -443,8 +443,16 @@ const rowHeights = ref([]); const columnWidths = ref([]); watch(() => props.rows, (newRows) => { // rows are set to null when new records are loading - rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight); - columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)]; + if (!newRows) return; +nextTick(() => { + if (rowRefs.value) { + rowHeights.value = rowRefs.value.map((el: HTMLElement) => el.offsetHeight); + } + if (headerRefs.value) { + // 48 - это примерная ширина чекбокса, если он есть + columnWidths.value = [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)]; + } + }); }); function addToCheckedValues(id: any) { From a90449421089ed1d9eee4b0ba2f976ebe12d51c8 Mon Sep 17 00:00:00 2001 From: tesaide Date: Fri, 5 Dec 2025 11:45:29 +0200 Subject: [PATCH 2/3] fix: magic number replacement --- .../spa/src/components/ResourceListTableVirtual.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/adminforth/spa/src/components/ResourceListTableVirtual.vue b/adminforth/spa/src/components/ResourceListTableVirtual.vue index cae28a62..8a7fdc06 100644 --- a/adminforth/spa/src/components/ResourceListTableVirtual.vue +++ b/adminforth/spa/src/components/ResourceListTableVirtual.vue @@ -19,7 +19,7 @@ - + ('rowRefs'); const headerRefs = useTemplateRef('headerRefs'); const rowHeights = ref([]); const columnWidths = ref([]); +const checkboxHeaderRef = useTemplateRef('checkboxHeaderRef'); watch(() => props.rows, (newRows) => { // rows are set to null when new records are loading if (!newRows) return; @@ -449,8 +450,10 @@ nextTick(() => { rowHeights.value = rowRefs.value.map((el: HTMLElement) => el.offsetHeight); } if (headerRefs.value) { - // 48 - это примерная ширина чекбокса, если он есть - columnWidths.value = [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)]; + columnWidths.value = [ + checkboxHeaderRef.value?.offsetWidth || 48, + ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth) + ]; } }); }); From bfc8a9bb62f610142601a42bf44dfee7548c3399 Mon Sep 17 00:00:00 2001 From: tesaide Date: Fri, 5 Dec 2025 12:50:37 +0200 Subject: [PATCH 3/3] fix: magic number solution --- .../spa/src/components/ResourceListTableVirtual.vue | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/adminforth/spa/src/components/ResourceListTableVirtual.vue b/adminforth/spa/src/components/ResourceListTableVirtual.vue index 8a7fdc06..fb952a8c 100644 --- a/adminforth/spa/src/components/ResourceListTableVirtual.vue +++ b/adminforth/spa/src/components/ResourceListTableVirtual.vue @@ -19,7 +19,7 @@ - + ('rowRefs'); const headerRefs = useTemplateRef('headerRefs'); const rowHeights = ref([]); const columnWidths = ref([]); -const checkboxHeaderRef = useTemplateRef('checkboxHeaderRef'); +const columnWidth = 48; watch(() => props.rows, (newRows) => { // rows are set to null when new records are loading if (!newRows) return; @@ -450,10 +450,7 @@ nextTick(() => { rowHeights.value = rowRefs.value.map((el: HTMLElement) => el.offsetHeight); } if (headerRefs.value) { - columnWidths.value = [ - checkboxHeaderRef.value?.offsetWidth || 48, - ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth) - ]; + columnWidths.value = [columnWidth, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)]; } }); });