@wyxos/vibe 1.6.29 → 2.0.2

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.
Files changed (43) hide show
  1. package/README.md +29 -287
  2. package/lib/index.cjs +1 -0
  3. package/lib/index.js +795 -1791
  4. package/lib/logo-dark.svg +36 -36
  5. package/lib/logo-light.svg +29 -29
  6. package/lib/logo.svg +32 -32
  7. package/lib/manifest.json +1 -1
  8. package/package.json +82 -96
  9. package/LICENSE +0 -21
  10. package/lib/vibe.css +0 -1
  11. package/lib/vite.svg +0 -1
  12. package/src/App.vue +0 -35
  13. package/src/Masonry.vue +0 -1030
  14. package/src/archive/App.vue +0 -96
  15. package/src/archive/InfiniteMansonry.spec.ts +0 -10
  16. package/src/archive/InfiniteMasonry.vue +0 -218
  17. package/src/assets/vue.svg +0 -1
  18. package/src/calculateLayout.ts +0 -194
  19. package/src/components/CodeTabs.vue +0 -158
  20. package/src/components/MasonryItem.vue +0 -499
  21. package/src/components/examples/BasicExample.vue +0 -46
  22. package/src/components/examples/CustomItemExample.vue +0 -87
  23. package/src/components/examples/HeaderFooterExample.vue +0 -79
  24. package/src/components/examples/ManualInitExample.vue +0 -78
  25. package/src/components/examples/SwipeModeExample.vue +0 -40
  26. package/src/createMasonryTransitions.ts +0 -176
  27. package/src/main.ts +0 -6
  28. package/src/masonryUtils.ts +0 -96
  29. package/src/pages.json +0 -36402
  30. package/src/router/index.ts +0 -20
  31. package/src/style.css +0 -32
  32. package/src/types.ts +0 -101
  33. package/src/useMasonryDimensions.ts +0 -59
  34. package/src/useMasonryItems.ts +0 -231
  35. package/src/useMasonryLayout.ts +0 -164
  36. package/src/useMasonryPagination.ts +0 -539
  37. package/src/useMasonryScroll.ts +0 -61
  38. package/src/useMasonryVirtualization.ts +0 -140
  39. package/src/useSwipeMode.ts +0 -233
  40. package/src/utils/errorHandler.ts +0 -8
  41. package/src/views/Examples.vue +0 -323
  42. package/src/views/Home.vue +0 -321
  43. package/toggle-link.mjs +0 -92
@@ -1,79 +0,0 @@
1
- <template>
2
- <Masonry
3
- v-model:items="items"
4
- :get-page="getPage"
5
- :load-at-page="1"
6
- :layout="layout"
7
- init="auto"
8
- >
9
- <template #item-header="{ item }">
10
- <div class="h-full flex items-center justify-between px-3">
11
- <div class="flex items-center gap-2">
12
- <div class="w-6 h-6 rounded-full bg-white/80 backdrop-blur-sm flex items-center justify-center shadow-sm">
13
- <i :class="item.type === 'video' ? 'fas fa-video text-[10px] text-slate-500' : 'fas fa-image text-[10px] text-slate-500'"></i>
14
- </div>
15
- <span class="text-xs font-medium text-slate-700">#{{ String(item.id).split('-')[0] }}</span>
16
- </div>
17
- <span v-if="item.title" class="text-[11px] text-slate-600 truncate max-w-[160px]">{{ item.title }}</span>
18
- </div>
19
- </template>
20
-
21
- <template #item-footer="{ item, remove }">
22
- <div class="h-full flex items-center justify-between px-3">
23
- <div class="flex items-center gap-2">
24
- <button
25
- v-if="remove"
26
- class="px-2.5 py-1 rounded-full bg-white/90 text-slate-700 text-[11px] shadow-sm hover:bg-red-500 hover:text-white transition-colors"
27
- @click.stop="remove(item)"
28
- >
29
- Remove
30
- </button>
31
- </div>
32
- <div class="text-[11px] text-slate-600">
33
- {{ item.width }}×{{ item.height }}
34
- </div>
35
- </div>
36
- </template>
37
- </Masonry>
38
- </template>
39
-
40
- <script setup lang="ts">
41
- import { ref } from 'vue'
42
- import Masonry from '../../Masonry.vue'
43
- import fixture from '../../pages.json'
44
- import type { MasonryItem, GetPageResult } from '../../types'
45
-
46
- const items = ref<MasonryItem[]>([])
47
-
48
- const layout = {
49
- sizes: { base: 1, sm: 2, md: 3, lg: 4 },
50
- gutterX: 10,
51
- gutterY: 10,
52
- header: 36,
53
- footer: 40
54
- }
55
-
56
- const getPage = async (page: number): Promise<GetPageResult> => {
57
- return new Promise((resolve) => {
58
- setTimeout(() => {
59
- const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined
60
-
61
- if (!pageData) {
62
- resolve({
63
- items: [],
64
- nextPage: null
65
- })
66
- return
67
- }
68
-
69
- resolve({
70
- items: pageData.items,
71
- nextPage: page < (fixture as any[]).length ? page + 1 : null
72
- })
73
- }, 300)
74
- })
75
- }
76
- </script>
77
-
78
-
79
-
@@ -1,78 +0,0 @@
1
- <template>
2
- <div class="relative h-full">
3
- <div
4
- v-if="!isInitialized"
5
- class="absolute inset-0 z-10 flex items-center justify-center bg-slate-900/40 backdrop-blur-sm"
6
- >
7
- <button
8
- type="button"
9
- class="inline-flex items-center gap-2 rounded-full bg-white px-5 py-2 text-sm font-semibold text-slate-900 shadow-sm ring-1 ring-slate-200 transition hover:-translate-y-0.5 hover:shadow-md"
10
- @click="handleInit"
11
- >
12
- Load gallery
13
- <span class="text-slate-400">-></span>
14
- </button>
15
- </div>
16
-
17
- <Masonry
18
- ref="masonryRef"
19
- v-model:items="items"
20
- :get-page="getPage"
21
- :load-at-page="1"
22
- :layout="layout"
23
- init="manual"
24
- />
25
- </div>
26
- </template>
27
-
28
- <script setup lang="ts">
29
- import { ref } from 'vue'
30
- import Masonry from '../../Masonry.vue'
31
- import fixture from '../../pages.json'
32
- import type { MasonryItem, GetPageResult } from '../../types'
33
-
34
- const items = ref<MasonryItem[]>([])
35
- const isInitialized = ref(false)
36
- const masonryRef = ref<InstanceType<typeof Masonry> | null>(null)
37
-
38
- const layout = {
39
- sizes: { base: 1, sm: 2, md: 3, lg: 4 },
40
- gutterX: 10,
41
- gutterY: 10
42
- }
43
-
44
- const getPage = async (page: number): Promise<GetPageResult> => {
45
- return new Promise((resolve) => {
46
- setTimeout(() => {
47
- const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined
48
-
49
- if (!pageData) {
50
- resolve({
51
- items: [],
52
- nextPage: null
53
- })
54
- return
55
- }
56
-
57
- resolve({
58
- items: pageData.items,
59
- nextPage: page < (fixture as any[]).length ? page + 1 : null
60
- })
61
- }, 300)
62
- })
63
- }
64
-
65
- function handleInit() {
66
- if (isInitialized.value) {
67
- return
68
- }
69
-
70
- isInitialized.value = true
71
- masonryRef.value?.loadPage(1)
72
- }
73
- </script>
74
-
75
-
76
-
77
-
78
-
@@ -1,40 +0,0 @@
1
- <template>
2
- <Masonry
3
- v-model:items="items"
4
- :get-page="getPage"
5
- :load-at-page="1"
6
- layout-mode="swipe"
7
- init="auto"
8
- />
9
- </template>
10
-
11
- <script setup lang="ts">
12
- import { ref } from 'vue'
13
- import Masonry from '../../Masonry.vue'
14
- import fixture from '../../pages.json'
15
- import type { MasonryItem, GetPageResult } from '../../types'
16
-
17
- const items = ref<MasonryItem[]>([])
18
-
19
- const getPage = async (page: number): Promise<GetPageResult> => {
20
- return new Promise((resolve) => {
21
- setTimeout(() => {
22
- const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined
23
-
24
- if (!pageData) {
25
- resolve({
26
- items: [],
27
- nextPage: null
28
- })
29
- return
30
- }
31
-
32
- resolve({
33
- items: pageData.items,
34
- nextPage: page < (fixture as any[]).length ? page + 1 : null
35
- })
36
- }, 300)
37
- })
38
- }
39
- </script>
40
-
@@ -1,176 +0,0 @@
1
- /**
2
- * Factory for masonry item transitions (typed)
3
- * Optimized for large item arrays by skipping DOM operations for items outside viewport
4
- */
5
- export function createMasonryTransitions(
6
- refs: { container?: any; masonry?: any },
7
- opts?: { leaveDurationMs?: number; virtualizing?: { value: boolean } }
8
- ) {
9
- // Cache viewport bounds to avoid repeated calculations
10
- let cachedViewportTop = 0
11
- let cachedViewportBottom = 0
12
- let cachedViewportHeight = 0
13
- const VIEWPORT_BUFFER_PX = 1000 // Buffer zone for items near viewport
14
-
15
- // Check if item is in viewport (with buffer) - optimized to skip DOM reads
16
- function isItemInViewport(itemTop: number, itemHeight: number): boolean {
17
- // Update cached viewport bounds if container exists
18
- const container = refs.container?.value
19
- if (container) {
20
- const scrollTop = container.scrollTop
21
- const clientHeight = container.clientHeight
22
- cachedViewportTop = scrollTop - VIEWPORT_BUFFER_PX
23
- cachedViewportBottom = scrollTop + clientHeight + VIEWPORT_BUFFER_PX
24
- cachedViewportHeight = clientHeight
25
- }
26
-
27
- const itemBottom = itemTop + itemHeight
28
- return itemBottom >= cachedViewportTop && itemTop <= cachedViewportBottom
29
- }
30
-
31
- function onEnter(el: HTMLElement, done: () => void) {
32
- const left = parseInt(el.dataset.left || '0', 10)
33
- const top = parseInt(el.dataset.top || '0', 10)
34
- const index = parseInt(el.dataset.index || '0', 10)
35
- // Get height from computed style or use a reasonable fallback
36
- const height = el.offsetHeight || parseInt(getComputedStyle(el).height || '200', 10) || 200
37
-
38
- // Skip animation during virtualization - just set position immediately
39
- if (opts?.virtualizing?.value) {
40
- el.style.transition = 'none'
41
- el.style.opacity = '1'
42
- el.style.transform = `translate3d(${left}px, ${top}px, 0) scale(1)`
43
- el.style.removeProperty('--masonry-opacity-delay')
44
- el.style.transition = ''
45
- done()
46
- return
47
- }
48
-
49
- // Skip animation for items outside viewport - just set position immediately
50
- if (!isItemInViewport(top, height)) {
51
- el.style.opacity = '1'
52
- el.style.transform = `translate3d(${left}px, ${top}px, 0) scale(1)`
53
- el.style.transition = 'none'
54
- done()
55
- return
56
- }
57
-
58
- const delay = Math.min(index * 20, 160)
59
- const prevOpacityDelay = el.style.getPropertyValue('--masonry-opacity-delay')
60
- el.style.setProperty('--masonry-opacity-delay', `${delay}ms`)
61
-
62
- el.style.opacity = '1'
63
- el.style.transform = `translate3d(${left}px, ${top}px, 0) scale(1)`
64
- const clear = () => {
65
- if (prevOpacityDelay) {
66
- el.style.setProperty('--masonry-opacity-delay', prevOpacityDelay)
67
- } else {
68
- el.style.removeProperty('--masonry-opacity-delay')
69
- }
70
- el.removeEventListener('transitionend', clear)
71
- done()
72
- }
73
- el.addEventListener('transitionend', clear)
74
- }
75
-
76
- function onBeforeEnter(el: HTMLElement) {
77
- const left = parseInt(el.dataset.left || '0', 10)
78
- const top = parseInt(el.dataset.top || '0', 10)
79
-
80
- // Skip animation during virtualization
81
- if (opts?.virtualizing?.value) {
82
- el.style.transition = 'none'
83
- el.style.opacity = '1'
84
- el.style.transform = `translate3d(${left}px, ${top}px, 0) scale(1)`
85
- el.style.removeProperty('--masonry-opacity-delay')
86
- return
87
- }
88
-
89
- el.style.opacity = '0'
90
- el.style.transform = `translate3d(${left}px, ${top + 10}px, 0) scale(0.985)`
91
- }
92
-
93
- function onBeforeLeave(el: HTMLElement) {
94
- const left = parseInt(el.dataset.left || '0', 10)
95
- const top = parseInt(el.dataset.top || '0', 10)
96
- const height = el.offsetHeight || parseInt(getComputedStyle(el).height || '200', 10) || 200
97
-
98
- // Skip animation during virtualization
99
- if (opts?.virtualizing?.value) {
100
- // no-op; removal will be immediate in leave
101
- return
102
- }
103
-
104
- // Skip animation for items outside viewport
105
- if (!isItemInViewport(top, height)) {
106
- el.style.transition = 'none'
107
- return
108
- }
109
-
110
- el.style.transition = 'none'
111
- el.style.opacity = '1'
112
- el.style.transform = `translate3d(${left}px, ${top}px, 0) scale(1)`
113
- el.style.removeProperty('--masonry-opacity-delay')
114
- el.style.transition = ''
115
- }
116
-
117
- function onLeave(el: HTMLElement, done: () => void) {
118
- const left = parseInt(el.dataset.left || '0', 10)
119
- const top = parseInt(el.dataset.top || '0', 10)
120
- const height = el.offsetHeight || parseInt(getComputedStyle(el).height || '200', 10) || 200
121
-
122
- // Skip animation during virtualization - remove immediately
123
- if (opts?.virtualizing?.value) {
124
- done()
125
- return
126
- }
127
-
128
- // Skip animation for items outside viewport - remove immediately
129
- if (!isItemInViewport(top, height)) {
130
- el.style.transition = 'none'
131
- el.style.opacity = '0'
132
- done()
133
- return
134
- }
135
-
136
- // Prefer explicit option, fallback to CSS variable for safety
137
- const fromOpts = typeof opts?.leaveDurationMs === 'number' ? opts!.leaveDurationMs : Number.NaN
138
- let leaveMs = Number.isFinite(fromOpts) && fromOpts > 0 ? fromOpts : Number.NaN
139
- if (!Number.isFinite(leaveMs)) {
140
- const cs = getComputedStyle(el)
141
- const varVal = cs.getPropertyValue('--masonry-leave-duration') || ''
142
- const parsed = parseFloat(varVal)
143
- leaveMs = Number.isFinite(parsed) && parsed > 0 ? parsed : 200
144
- }
145
-
146
- const prevDuration = el.style.transitionDuration
147
-
148
- const cleanup = () => {
149
- el.removeEventListener('transitionend', onEnd as any)
150
- clearTimeout(fallback)
151
- el.style.transitionDuration = prevDuration || ''
152
- }
153
- const onEnd = (e?: Event) => {
154
- if (!e || e.target === el) {
155
- cleanup()
156
- done()
157
- }
158
- }
159
- const fallback = setTimeout(() => {
160
- cleanup()
161
- done()
162
- }, leaveMs + 100)
163
-
164
- el.style.transitionDuration = `${leaveMs}ms`
165
- el.style.opacity = '0'
166
- el.style.transform = `translate3d(${left}px, ${top + 10}px, 0) scale(0.985)`
167
- el.addEventListener('transitionend', onEnd as any)
168
- }
169
-
170
- return {
171
- onEnter,
172
- onBeforeEnter,
173
- onBeforeLeave,
174
- onLeave
175
- }
176
- }
package/src/main.ts DELETED
@@ -1,6 +0,0 @@
1
- import { createApp } from 'vue'
2
- import './style.css'
3
- import App from './App.vue'
4
- import router from './router'
5
-
6
- createApp(App).use(router).mount('#app')
@@ -1,96 +0,0 @@
1
- import type { LayoutOptions, ProcessedMasonryItem } from './types'
2
-
3
- /**
4
- * Get responsive column count based on container width and layout sizes
5
- */
6
- export function getColumnCount(layout: Pick<LayoutOptions, 'sizes'> & { sizes: Required<NonNullable<LayoutOptions['sizes']>> }, containerWidth?: number): number {
7
- const width = containerWidth ?? (typeof window !== 'undefined' ? window.innerWidth : 1024)
8
- const sizes = layout.sizes
9
-
10
- if (width >= 1536 && sizes['2xl']) return sizes['2xl']
11
- if (width >= 1280 && sizes.xl) return sizes.xl
12
- if (width >= 1024 && sizes.lg) return sizes.lg
13
- if (width >= 768 && sizes.md) return sizes.md
14
- if (width >= 640 && sizes.sm) return sizes.sm
15
- return sizes.base
16
- }
17
-
18
- /**
19
- * Get current breakpoint name based on container width
20
- */
21
- export function getBreakpointName(containerWidth?: number): 'base' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' {
22
- // Only use fallback if containerWidth is explicitly undefined/null
23
- // If it's 0, we still use it (will return 'base')
24
- const width = containerWidth !== undefined && containerWidth !== null
25
- ? containerWidth
26
- : (typeof window !== 'undefined' ? window.innerWidth : 1024)
27
-
28
- if (width >= 1536) return '2xl'
29
- if (width >= 1280) return 'xl'
30
- if (width >= 1024) return 'lg'
31
- if (width >= 768) return 'md'
32
- if (width >= 640) return 'sm'
33
- return 'base'
34
- }
35
-
36
- /**
37
- * Calculate container height based on item positions
38
- */
39
- export function calculateContainerHeight(items: ProcessedMasonryItem[]): number {
40
- const contentHeight = items.reduce((acc, item) => {
41
- return Math.max(acc, item.top + item.columnHeight)
42
- }, 0)
43
- return contentHeight + 500
44
- }
45
-
46
- /**
47
- * Get style object for masonry item positioning
48
- */
49
- export function getItemStyle(item: ProcessedMasonryItem): Record<string, string> {
50
- return {
51
- transform: `translate3d(${item.left}px, ${item.top}px, 0)` ,
52
- top: '0px',
53
- left: '0px',
54
- width: `${item.columnWidth}px`,
55
- height: `${item.columnHeight}px`
56
- }
57
- }
58
-
59
- /**
60
- * Get item attributes for rendering
61
- */
62
- export function getItemAttributes(item: ProcessedMasonryItem, index: number = 0): Record<string, any> {
63
- return {
64
- style: getItemStyle(item),
65
- 'data-top': item.top,
66
- 'data-left': item.left,
67
- 'data-id': `${(item as any).page}-${(item as any).id}`,
68
- 'data-index': index,
69
- }
70
- }
71
-
72
- /**
73
- * Calculate column heights for masonry layout
74
- */
75
- export function calculateColumnHeights(items: ProcessedMasonryItem[], columnCount: number): number[] {
76
- // Derive columns by actual left positions to reflect shortest-column placement
77
- if (!items.length || columnCount <= 0) {
78
- return new Array<number>(Math.max(1, columnCount)).fill(0)
79
- }
80
- // Unique lefts (sorted) represent the columns in visual order
81
- const uniqueLefts = Array.from(new Set(items.map(i => i.left))).sort((a, b) => a - b)
82
- const limitedLefts = uniqueLefts.slice(0, columnCount)
83
- const leftIndexMap = new Map<number, number>()
84
- for (let idx = 0; idx < limitedLefts.length; idx++) leftIndexMap.set(limitedLefts[idx], idx)
85
-
86
- const heights = new Array<number>(limitedLefts.length).fill(0)
87
- for (const it of items) {
88
- const col = leftIndexMap.get(it.left)
89
- if (col != null) {
90
- heights[col] = Math.max(heights[col], it.top + it.columnHeight)
91
- }
92
- }
93
- // Pad if some columns haven't been populated yet (e.g., initial render)
94
- while (heights.length < columnCount) heights.push(0)
95
- return heights
96
- }