@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wyxos/vibe",
3
- "version": "1.2.11",
3
+ "version": "1.2.12",
4
4
  "main": "index.js",
5
5
  "module": "index.js",
6
6
  "type": "module",
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: fixture[page - 1].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: 1
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
- paginationHistory.value = [props.loadAtPage]
241
-
242
- const response = await getContent(paginationHistory.value[0])
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
- paginationHistory.value.push(response.nextPage)
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
 
@@ -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 + footer + header;
59
+ const originalHeight = item.height;
60
60
 
61
61
  newItem.columnWidth = columnWidth;
62
62
  newItem.left = col * (columnWidth + gutterX);
63
- newItem.columnHeight = Math.round((columnWidth * originalHeight) / originalWidth);
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
- })