mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 07:30:11 +00:00
revert: simplify VirtualGrid item size measurement
This commit is contained in:
@@ -105,64 +105,6 @@ describe('VirtualGrid', () => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('uses measured row step (including gap) to compute visible range', async () => {
|
||||
const items = createItems(100)
|
||||
mockedWidth.value = 100
|
||||
mockedHeight.value = 60
|
||||
mockedScrollY.value = 110
|
||||
|
||||
const wrapper = mount(VirtualGrid<TestItem>, {
|
||||
props: {
|
||||
items,
|
||||
gridStyle: defaultGridStyle,
|
||||
defaultItemHeight: 50,
|
||||
defaultItemWidth: 100,
|
||||
maxColumns: 1,
|
||||
bufferRows: 0
|
||||
},
|
||||
slots: {
|
||||
item: `<template #item="{ index }">
|
||||
<div class="test-index">{{ index }}</div>
|
||||
</template>`
|
||||
},
|
||||
attachTo: document.body
|
||||
})
|
||||
|
||||
await nextTick()
|
||||
|
||||
const initialIndices = wrapper
|
||||
.findAll('.test-index')
|
||||
.map((node) => node.text())
|
||||
expect(initialIndices[0]).toBe('2')
|
||||
|
||||
const renderedItemEls = wrapper
|
||||
.findAll<HTMLElement>('[data-virtual-grid-item]')
|
||||
.map((node) => node.element)
|
||||
|
||||
expect(renderedItemEls.length).toBeGreaterThanOrEqual(2)
|
||||
|
||||
Object.defineProperty(renderedItemEls[0], 'clientHeight', { value: 50 })
|
||||
Object.defineProperty(renderedItemEls[0], 'clientWidth', { value: 100 })
|
||||
Object.defineProperty(renderedItemEls[0], 'offsetTop', { value: 0 })
|
||||
Object.defineProperty(renderedItemEls[0], 'offsetLeft', { value: 0 })
|
||||
|
||||
Object.defineProperty(renderedItemEls[1], 'clientHeight', { value: 50 })
|
||||
Object.defineProperty(renderedItemEls[1], 'clientWidth', { value: 100 })
|
||||
Object.defineProperty(renderedItemEls[1], 'offsetTop', { value: 60 })
|
||||
Object.defineProperty(renderedItemEls[1], 'offsetLeft', { value: 0 })
|
||||
|
||||
await wrapper.setProps({ items: [...items] })
|
||||
await nextTick()
|
||||
await nextTick()
|
||||
|
||||
const updatedIndices = wrapper
|
||||
.findAll('.test-index')
|
||||
.map((node) => node.text())
|
||||
expect(updatedIndices[0]).toBe('1')
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('respects maxColumns prop', async () => {
|
||||
const items = createItems(10)
|
||||
mockedWidth.value = 400
|
||||
|
||||
@@ -57,8 +57,8 @@ const emit = defineEmits<{
|
||||
'approach-end': []
|
||||
}>()
|
||||
|
||||
const rowHeight = ref(defaultItemHeight)
|
||||
const colWidth = ref(defaultItemWidth)
|
||||
const itemHeight = ref(defaultItemHeight)
|
||||
const itemWidth = ref(defaultItemWidth)
|
||||
const container = ref<HTMLElement | null>(null)
|
||||
const { width, height } = useElementSize(container)
|
||||
const { y: scrollY } = useScroll(container, {
|
||||
@@ -67,7 +67,7 @@ const { y: scrollY } = useScroll(container, {
|
||||
})
|
||||
|
||||
const cols = computed(() =>
|
||||
Math.min(Math.floor(width.value / colWidth.value) || 1, maxColumns)
|
||||
Math.min(Math.floor(width.value / itemWidth.value) || 1, maxColumns)
|
||||
)
|
||||
|
||||
const mergedGridStyle = computed<CSSProperties>(() => {
|
||||
@@ -78,8 +78,8 @@ const mergedGridStyle = computed<CSSProperties>(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const viewRows = computed(() => Math.ceil(height.value / rowHeight.value))
|
||||
const offsetRows = computed(() => Math.floor(scrollY.value / rowHeight.value))
|
||||
const viewRows = computed(() => Math.ceil(height.value / itemHeight.value))
|
||||
const offsetRows = computed(() => Math.floor(scrollY.value / itemHeight.value))
|
||||
const isValidGrid = computed(() => height.value && width.value && items?.length)
|
||||
|
||||
const state = computed<GridState>(() => {
|
||||
@@ -101,28 +101,15 @@ const renderedItems = computed(() =>
|
||||
isValidGrid.value ? items.slice(state.value.start, state.value.end) : []
|
||||
)
|
||||
|
||||
function spacerRowsToHeight(rows: number): string {
|
||||
return `${rows * rowHeight.value}px`
|
||||
function rowsToHeight(rows: number): string {
|
||||
return `${(rows / cols.value) * itemHeight.value}px`
|
||||
}
|
||||
|
||||
const topSpacerRows = computed(() => {
|
||||
if (!isValidGrid.value) return 0
|
||||
return Math.floor(state.value.start / cols.value)
|
||||
})
|
||||
|
||||
const bottomSpacerRows = computed(() => {
|
||||
if (!isValidGrid.value) return 0
|
||||
|
||||
const totalRows = Math.ceil(items.length / cols.value)
|
||||
const renderedEndRow = Math.ceil(state.value.end / cols.value)
|
||||
return Math.max(0, totalRows - renderedEndRow)
|
||||
})
|
||||
|
||||
const topSpacerStyle = computed<CSSProperties>(() => ({
|
||||
height: spacerRowsToHeight(topSpacerRows.value)
|
||||
height: rowsToHeight(state.value.start)
|
||||
}))
|
||||
const bottomSpacerStyle = computed<CSSProperties>(() => ({
|
||||
height: spacerRowsToHeight(bottomSpacerRows.value)
|
||||
height: rowsToHeight(items.length - state.value.end)
|
||||
}))
|
||||
|
||||
whenever(
|
||||
@@ -132,53 +119,19 @@ whenever(
|
||||
}
|
||||
)
|
||||
|
||||
const ITEM_SIZE_EPSILON_PX = 1
|
||||
|
||||
/**
|
||||
* Measures the effective grid row/column step (including `gap`) from rendered
|
||||
* items to keep spacer math stable and prevent scroll jitter near the end.
|
||||
*/
|
||||
function updateItemSize(): void {
|
||||
if (!container.value) return
|
||||
if (container.value) {
|
||||
const firstItem = container.value.querySelector('[data-virtual-grid-item]')
|
||||
|
||||
const itemElements = Array.from(
|
||||
container.value.querySelectorAll('[data-virtual-grid-item]')
|
||||
).filter((node): node is HTMLElement => node instanceof HTMLElement)
|
||||
// Don't update item size if the first item is not rendered yet
|
||||
if (!firstItem?.clientHeight || !firstItem?.clientWidth) return
|
||||
|
||||
const firstItem = itemElements[0]
|
||||
|
||||
if (!firstItem?.clientHeight || !firstItem?.clientWidth) return
|
||||
|
||||
const nextRowItem = itemElements.find(
|
||||
(item) => item.offsetTop > firstItem.offsetTop
|
||||
)
|
||||
|
||||
const measuredRowHeight = nextRowItem
|
||||
? nextRowItem.offsetTop - firstItem.offsetTop
|
||||
: firstItem.clientHeight
|
||||
|
||||
const nextColItem = itemElements.find(
|
||||
(item) =>
|
||||
item.offsetTop === firstItem.offsetTop &&
|
||||
item.offsetLeft > firstItem.offsetLeft
|
||||
)
|
||||
|
||||
const measuredColWidth = nextColItem
|
||||
? nextColItem.offsetLeft - firstItem.offsetLeft
|
||||
: firstItem.clientWidth
|
||||
|
||||
if (
|
||||
measuredRowHeight > 0 &&
|
||||
Math.abs(rowHeight.value - measuredRowHeight) >= ITEM_SIZE_EPSILON_PX
|
||||
) {
|
||||
rowHeight.value = measuredRowHeight
|
||||
}
|
||||
|
||||
if (
|
||||
measuredColWidth > 0 &&
|
||||
Math.abs(colWidth.value - measuredColWidth) >= ITEM_SIZE_EPSILON_PX
|
||||
) {
|
||||
colWidth.value = measuredColWidth
|
||||
if (itemHeight.value !== firstItem.clientHeight) {
|
||||
itemHeight.value = firstItem.clientHeight
|
||||
}
|
||||
if (itemWidth.value !== firstItem.clientWidth) {
|
||||
itemWidth.value = firstItem.clientWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
const onResize = debounce(updateItemSize, resizeDebounce)
|
||||
|
||||
Reference in New Issue
Block a user