@wyxos/vibe 1.2.11 → 1.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/App.vue +15 -3
- package/src/Masonry.vue +19 -6
- package/src/calculateLayout.js +3 -2
- package/src/calculateLayout.test.js +0 -48
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -10,9 +10,21 @@ const masonry = ref(null)
|
|
|
10
10
|
const getPage = async (page) => {
|
|
11
11
|
return new Promise((resolve) => {
|
|
12
12
|
setTimeout(() => {
|
|
13
|
+
// Check if the page exists in the fixture
|
|
14
|
+
const pageData = fixture[page - 1];
|
|
15
|
+
|
|
16
|
+
if (!pageData) {
|
|
17
|
+
// Return empty items if page doesn't exist
|
|
18
|
+
resolve({
|
|
19
|
+
items: [],
|
|
20
|
+
nextPage: null // null indicates no more pages
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
let output = {
|
|
14
|
-
items:
|
|
15
|
-
nextPage: page + 1
|
|
26
|
+
items: pageData.items,
|
|
27
|
+
nextPage: page < fixture.length ? page + 1 : null
|
|
16
28
|
};
|
|
17
29
|
|
|
18
30
|
resolve(output)
|
|
@@ -35,7 +47,7 @@ const getPage = async (page) => {
|
|
|
35
47
|
<p>Loading: <span class="bg-blue-500 text-white p-2 rounded">{{ masonry.isLoading }}</span></p>
|
|
36
48
|
</div>
|
|
37
49
|
</header>
|
|
38
|
-
<masonry class="bg-blue-500 " v-model:items="items" :get-next-page="getPage" ref="masonry">
|
|
50
|
+
<masonry class="bg-blue-500 " v-model:items="items" :get-next-page="getPage" :load-at-page="1" ref="masonry">
|
|
39
51
|
<template #item="{item, onRemove}">
|
|
40
52
|
<img :src="item.src" class="w-full"/>
|
|
41
53
|
<button class="absolute bottom-0 right-0 bg-red-500 text-white p-2 rounded cursor-pointer" @click="onRemove(item)">
|
package/src/Masonry.vue
CHANGED
|
@@ -10,8 +10,8 @@ const props = defineProps({
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
loadAtPage: {
|
|
13
|
-
type: Number,
|
|
14
|
-
default:
|
|
13
|
+
type: [Number, String],
|
|
14
|
+
default: null
|
|
15
15
|
},
|
|
16
16
|
items: {
|
|
17
17
|
type: Array,
|
|
@@ -24,6 +24,10 @@ const props = defineProps({
|
|
|
24
24
|
type: String,
|
|
25
25
|
default: 'page', // or 'cursor'
|
|
26
26
|
validator: v => ['page', 'cursor'].includes(v)
|
|
27
|
+
},
|
|
28
|
+
skipInitialLoad: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
default: false
|
|
27
31
|
}
|
|
28
32
|
})
|
|
29
33
|
|
|
@@ -80,6 +84,7 @@ defineExpose({
|
|
|
80
84
|
refreshLayout,
|
|
81
85
|
containerHeight,
|
|
82
86
|
onRemove,
|
|
87
|
+
loadNext
|
|
83
88
|
})
|
|
84
89
|
|
|
85
90
|
async function onScroll() {
|
|
@@ -237,11 +242,19 @@ onMounted(async () => {
|
|
|
237
242
|
|
|
238
243
|
columns.value = getColumnCount()
|
|
239
244
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
245
|
+
// For cursor-based pagination, loadAtPage can be null for the first request
|
|
246
|
+
const initialPage = props.loadAtPage
|
|
247
|
+
paginationHistory.value = [initialPage]
|
|
243
248
|
|
|
244
|
-
|
|
249
|
+
// Skip initial load if skipInitialLoad prop is true
|
|
250
|
+
if (!props.skipInitialLoad) {
|
|
251
|
+
const response = await getContent(paginationHistory.value[0])
|
|
252
|
+
paginationHistory.value.push(response.nextPage)
|
|
253
|
+
} else {
|
|
254
|
+
await nextTick()
|
|
255
|
+
// Just refresh the layout with any existing items
|
|
256
|
+
refreshLayout(masonry.value)
|
|
257
|
+
}
|
|
245
258
|
|
|
246
259
|
isLoading.value = false
|
|
247
260
|
|
package/src/calculateLayout.js
CHANGED
|
@@ -56,11 +56,12 @@ export default function calculateLayout(items, container, columnCount, options =
|
|
|
56
56
|
|
|
57
57
|
const col = index % columnCount;
|
|
58
58
|
const originalWidth = item.width;
|
|
59
|
-
const originalHeight = item.height
|
|
59
|
+
const originalHeight = item.height;
|
|
60
60
|
|
|
61
61
|
newItem.columnWidth = columnWidth;
|
|
62
62
|
newItem.left = col * (columnWidth + gutterX);
|
|
63
|
-
newItem.
|
|
63
|
+
newItem.imageHeight = Math.round((columnWidth * originalHeight) / originalWidth);
|
|
64
|
+
newItem.columnHeight = newItem.imageHeight + footer + header;
|
|
64
65
|
newItem.top = columnHeights[col];
|
|
65
66
|
|
|
66
67
|
columnHeights[col] += newItem.columnHeight + gutterY;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import calculateLayout from './calculateLayout'
|
|
3
|
-
import fixture from './pages.json'
|
|
4
|
-
|
|
5
|
-
describe('calculateLayout', () => {
|
|
6
|
-
it('should calculate correct layout positions for known inputs', () => {
|
|
7
|
-
const items = fixture[0].items.slice(0, 3) // first 3 items
|
|
8
|
-
const mockContainer = {
|
|
9
|
-
offsetWidth: 1000,
|
|
10
|
-
clientWidth: 980 // scrollbarWidth = 20
|
|
11
|
-
}
|
|
12
|
-
const columnCount = 3
|
|
13
|
-
const gutterX = 10
|
|
14
|
-
const gutterY = 10
|
|
15
|
-
|
|
16
|
-
const scrollbarWidth = mockContainer.offsetWidth - mockContainer.clientWidth
|
|
17
|
-
const usableWidth = mockContainer.offsetWidth - scrollbarWidth
|
|
18
|
-
const totalGutterX = gutterX * (columnCount - 1)
|
|
19
|
-
const expectedColumnWidth = Math.floor((usableWidth - totalGutterX) / columnCount)
|
|
20
|
-
|
|
21
|
-
const result = calculateLayout(items, mockContainer, columnCount, gutterX, gutterY)
|
|
22
|
-
|
|
23
|
-
expect(result.length).toBe(3)
|
|
24
|
-
|
|
25
|
-
result.forEach((item, i) => {
|
|
26
|
-
const original = items[i]
|
|
27
|
-
const expectedColumn = i % columnCount
|
|
28
|
-
const expectedLeft = expectedColumn * (expectedColumnWidth + gutterX)
|
|
29
|
-
const expectedHeight = Math.round((expectedColumnWidth * original.height) / original.width)
|
|
30
|
-
|
|
31
|
-
expect(item.left).toBe(expectedLeft)
|
|
32
|
-
expect(item.columnWidth).toBe(expectedColumnWidth)
|
|
33
|
-
expect(item.columnHeight).toBe(expectedHeight)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
// Test top stacking logic: second item in same column should be placed below with gutter
|
|
37
|
-
const secondBatch = fixture[0].items.slice(0, 6)
|
|
38
|
-
const stacked = calculateLayout(secondBatch, mockContainer, columnCount, gutterX, gutterY)
|
|
39
|
-
for (let col = 0; col < columnCount; col++) {
|
|
40
|
-
const colItems = stacked.filter((_, i) => i % columnCount === col)
|
|
41
|
-
for (let j = 1; j < colItems.length; j++) {
|
|
42
|
-
expect(colItems[j].top).toBe(
|
|
43
|
-
colItems[j - 1].top + colItems[j - 1].columnHeight + gutterY
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
})
|