@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,321 +0,0 @@
1
- <script setup lang="ts">
2
- import Masonry from "../Masonry.vue";
3
- import { ref, reactive, computed, watch, nextTick } from "vue";
4
- import fixture from "../pages.json";
5
- import type { MasonryItem, GetPageResult } from "../types";
6
-
7
- const items = ref<MasonryItem[]>([]);
8
-
9
- const masonry = ref<InstanceType<typeof Masonry> | null>(null);
10
-
11
- const layoutParams = reactive({
12
- sizes: {
13
- base: 1,
14
- sm: 2,
15
- md: 3,
16
- lg: 4,
17
- xl: 5,
18
- '2xl': 10
19
- },
20
- header: 0,
21
- footer: 40
22
- });
23
-
24
- const layout = computed(() => ({
25
- sizes: { ...layoutParams.sizes },
26
- header: layoutParams.header,
27
- footer: layoutParams.footer
28
- }));
29
-
30
- const showLayoutControls = ref(false);
31
-
32
- const getPage = async (page: number): Promise<GetPageResult> => {
33
- return new Promise((resolve) => {
34
- setTimeout(() => {
35
- // Check if the page exists in the fixture
36
- const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined;
37
-
38
- if (!pageData) {
39
- // Return empty items if page doesn't exist
40
- resolve({
41
- items: [],
42
- nextPage: null // null indicates no more pages
43
- });
44
- return;
45
- }
46
-
47
- const output: GetPageResult = {
48
- items: pageData.items,
49
- nextPage: page < (fixture as any[]).length ? page + 1 : null
50
- };
51
-
52
- resolve(output);
53
- }, 1000);
54
- });
55
- };
56
-
57
- // Device Simulation
58
- const deviceMode = ref<'auto' | 'phone' | 'tablet' | 'desktop'>('auto');
59
- const deviceOrientation = ref<'portrait' | 'landscape'>('portrait');
60
-
61
- const deviceDimensions = computed(() => {
62
- let baseDimensions: { width: number; height: number } | null = null;
63
-
64
- switch (deviceMode.value) {
65
- case 'phone':
66
- baseDimensions = { width: 375, height: 667 };
67
- break;
68
- case 'tablet':
69
- baseDimensions = { width: 768, height: 1024 };
70
- break;
71
- case 'desktop':
72
- // Desktop is typically landscape, but allow portrait too
73
- baseDimensions = { width: 1280, height: 720 };
74
- break;
75
- default:
76
- return null;
77
- }
78
-
79
- // Swap dimensions for landscape orientation (except desktop which is already landscape by default)
80
- if (deviceOrientation.value === 'landscape' && deviceMode.value !== 'desktop') {
81
- return { width: baseDimensions.height, height: baseDimensions.width };
82
- }
83
-
84
- // For desktop, swap if portrait is selected
85
- if (deviceMode.value === 'desktop' && deviceOrientation.value === 'portrait') {
86
- return { width: baseDimensions.height, height: baseDimensions.width };
87
- }
88
-
89
- return baseDimensions;
90
- });
91
-
92
- const containerStyle = computed(() => {
93
- const dimensions = deviceDimensions.value;
94
- if (!dimensions) {
95
- return { width: '100%', height: '100%' };
96
- }
97
-
98
- const borderRadius = deviceMode.value === 'phone' ? '20px' : deviceMode.value === 'tablet' ? '12px' : '8px';
99
-
100
- return {
101
- width: `${dimensions.width}px`,
102
- height: `${dimensions.height}px`,
103
- maxWidth: '100%',
104
- margin: '0 auto',
105
- border: '1px solid #e2e8f0',
106
- borderRadius,
107
- overflow: 'hidden'
108
- };
109
- });
110
-
111
- // Override container dimensions when device simulation is active
112
- watch([deviceMode, deviceOrientation, masonry], () => {
113
- if (masonry.value && deviceDimensions.value) {
114
- // Use nextTick to ensure masonry is fully initialized
115
- nextTick(() => {
116
- if (masonry.value && masonry.value.setFixedDimensions) {
117
- masonry.value.setFixedDimensions({
118
- width: deviceDimensions.value.width,
119
- height: deviceDimensions.value.height
120
- });
121
- }
122
- });
123
- } else if (masonry.value && !deviceDimensions.value) {
124
- // Clear fixed dimensions when in auto mode
125
- nextTick(() => {
126
- if (masonry.value && masonry.value.setFixedDimensions) {
127
- masonry.value.setFixedDimensions(null);
128
- }
129
- });
130
- }
131
- }, { immediate: true });
132
-
133
- // Add hover overlays to masonry items
134
- watch(() => items.value, () => {
135
- nextTick(() => {
136
- const masonryItems = document.querySelectorAll('.demo-masonry .masonry-item');
137
- masonryItems.forEach((el) => {
138
- if ((el as HTMLElement).hasAttribute('data-overlay-added')) return;
139
-
140
- const itemId = el.getAttribute('data-id');
141
- if (itemId) {
142
- const item = items.value.find((i: any) => `${i.page}-${i.id}` === itemId);
143
- if (item) {
144
- const container = el.querySelector('.relative.w-full.h-full.flex.flex-col') as HTMLElement;
145
- if (container && !container.querySelector('.demo-item-overlay')) {
146
- const overlay = document.createElement('div');
147
- overlay.className = 'demo-item-overlay absolute top-2 left-2 z-20 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none';
148
- overlay.innerHTML = `
149
- <div class="flex items-center gap-2 px-2 py-1 rounded-lg bg-black/70 backdrop-blur-sm text-white text-xs">
150
- <div class="w-4 h-4 rounded-full bg-white/20 flex items-center justify-center">
151
- <i class="fas ${item.type === 'video' ? 'fa-video' : 'fa-image'} text-[8px]"></i>
152
- </div>
153
- <span class="font-medium">#${String(item.id).split('-')[0]}</span>
154
- </div>
155
- `;
156
- container.classList.add('group');
157
- container.appendChild(overlay);
158
- (el as HTMLElement).setAttribute('data-overlay-added', 'true');
159
- }
160
- }
161
- }
162
- });
163
- });
164
- }, { deep: true });
165
- </script>
166
-
167
- <template>
168
- <main class="flex flex-col h-screen overflow-hidden bg-slate-50 relative pt-[112px]">
169
- <!-- Fixed Sub-Header -->
170
- <header
171
- class="fixed top-[53px] left-0 right-0 z-20 w-full bg-white/80 backdrop-blur-md border-b border-slate-200 shadow-sm transition-all duration-300">
172
- <div class="max-w-7xl mx-auto px-4 h-14 flex items-center justify-end">
173
- <!-- Right: Controls -->
174
- <div class="flex items-center gap-3">
175
- <!-- Status Pill -->
176
- <div v-if="masonry"
177
- class="hidden md:flex items-center gap-2 text-xs font-medium text-slate-600 bg-slate-50 px-3 py-1.5 rounded-lg border border-slate-100">
178
- <span class="flex items-center gap-1.5">
179
- <span class="w-1.5 h-1.5 rounded-full shadow-sm"
180
- :class="masonry.isLoading ? 'bg-amber-400 animate-pulse' : 'bg-emerald-400'"></span>
181
- {{ masonry.isLoading ? 'Loading...' : 'Ready' }}
182
- </span>
183
- <span class="w-px h-3 bg-slate-200"></span>
184
- <span>{{ items.length }} items</span>
185
- <span class="w-px h-3 bg-slate-200"></span>
186
- <span class="uppercase font-semibold text-slate-500">{{ masonry.currentBreakpoint }}</span>
187
- <span class="w-px h-3 bg-slate-200"></span>
188
- <span>{{ Math.round(masonry.containerWidth) }}×{{ Math.round(masonry.containerHeight) }}</span>
189
- <span v-if="masonry.currentPage != null" class="w-px h-3 bg-slate-200"></span>
190
- <span v-if="masonry.currentPage != null" class="text-slate-500">Page {{ masonry.currentPage }}</span>
191
- </div>
192
-
193
- <div class="h-8 w-px bg-slate-100 mx-1 hidden md:block"></div>
194
-
195
- <!-- Action Buttons -->
196
- <div class="flex items-center gap-1">
197
- <button @click="showLayoutControls = !showLayoutControls"
198
- class="w-9 h-9 flex items-center justify-center text-slate-500 hover:text-blue-600 hover:bg-blue-50 rounded-xl transition-all duration-200"
199
- :class="{ 'text-blue-600 bg-blue-50 ring-2 ring-blue-100': showLayoutControls }" title="Layout Settings">
200
- <i class="fas fa-sliders text-sm"></i>
201
- </button>
202
-
203
- <a href="https://github.com/wyxos/vibe" target="_blank"
204
- class="w-9 h-9 flex items-center justify-center text-slate-400 hover:text-slate-900 hover:bg-slate-50 rounded-xl transition-all duration-200"
205
- title="View on GitHub">
206
- <i class="fab fa-github text-lg"></i>
207
- </a>
208
- </div>
209
- </div>
210
- </div>
211
-
212
- <!-- Layout Controls Panel -->
213
- <transition enter-active-class="transition duration-200 ease-out"
214
- enter-from-class="transform -translate-y-2 opacity-0" enter-to-class="transform translate-y-0 opacity-100"
215
- leave-active-class="transition duration-150 ease-in" leave-from-class="transform translate-y-0 opacity-100"
216
- leave-to-class="transform -translate-y-2 opacity-0">
217
- <div v-if="showLayoutControls"
218
- class="absolute top-full left-4 right-4 md:left-auto md:right-4 mt-2 md:w-full md:max-w-lg bg-white/90 backdrop-blur-md border border-slate-200 shadow-xl rounded-xl p-4 md:p-6 pointer-events-auto z-30">
219
- <div class="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
220
- <!-- Column Settings -->
221
- <div>
222
- <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Column Configuration</h3>
223
- <div class="grid grid-cols-3 gap-2 sm:gap-3">
224
- <div v-for="(val, key) in layoutParams.sizes" :key="key" class="flex flex-col gap-1.5">
225
- <label class="text-[10px] font-bold text-slate-500 uppercase text-center">{{ key }}</label>
226
- <input v-model.number="layoutParams.sizes[key]" type="number" min="1"
227
- class="w-full px-1 py-2 bg-slate-50 border border-slate-200 rounded-lg text-center text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all" />
228
- </div>
229
- </div>
230
- </div>
231
-
232
- <!-- Spacing Settings -->
233
- <div>
234
- <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Spacing</h3>
235
- <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
236
- <div class="flex flex-col gap-1.5">
237
- <label class="text-[10px] font-bold text-slate-500 uppercase">Header Offset</label>
238
- <div class="relative">
239
- <input v-model.number="layoutParams.header" type="number" min="0"
240
- class="w-full pl-3 pr-8 py-2 bg-slate-50 border border-slate-200 rounded-lg text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all" />
241
- <span class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-400">px</span>
242
- </div>
243
- </div>
244
- <div class="flex flex-col gap-1.5">
245
- <label class="text-[10px] font-bold text-slate-500 uppercase">Footer Offset</label>
246
- <div class="relative">
247
- <input v-model.number="layoutParams.footer" type="number" min="0"
248
- class="w-full pl-3 pr-8 py-2 bg-slate-50 border border-slate-200 rounded-lg text-sm font-medium text-slate-700 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all" />
249
- <span class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-400">px</span>
250
- </div>
251
- </div>
252
- </div>
253
- </div>
254
-
255
- <!-- Device Simulation -->
256
- <div class="md:col-span-2 border-t border-slate-100 pt-6 mt-2">
257
- <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Device Simulation</h3>
258
- <div class="space-y-4">
259
- <div class="flex flex-wrap gap-2">
260
- <button v-for="mode in ['auto', 'phone', 'tablet', 'desktop']" :key="mode"
261
- @click="deviceMode = mode as any"
262
- class="px-4 py-2 rounded-lg text-sm font-medium transition-all capitalize"
263
- :class="deviceMode === mode ? 'bg-blue-600 text-white shadow-md shadow-blue-200' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'">
264
- <i class="fas mr-2" :class="{
265
- 'fa-desktop': mode === 'desktop' || mode === 'auto',
266
- 'fa-mobile-alt': mode === 'phone',
267
- 'fa-tablet-alt': mode === 'tablet'
268
- }"></i>
269
- {{ mode }}
270
- </button>
271
- </div>
272
- <div v-if="deviceMode !== 'auto'" class="flex flex-wrap gap-2">
273
- <button v-for="orientation in ['portrait', 'landscape']" :key="orientation"
274
- @click="deviceOrientation = orientation as any"
275
- class="px-4 py-2 rounded-lg text-sm font-medium transition-all capitalize"
276
- :class="deviceOrientation === orientation ? 'bg-blue-600 text-white shadow-md shadow-blue-200' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'">
277
- <i class="fas mr-2" :class="{
278
- 'fa-mobile-alt': orientation === 'portrait',
279
- 'fa-mobile-alt fa-rotate-90': orientation === 'landscape'
280
- }"></i>
281
- {{ orientation }}
282
- </button>
283
- </div>
284
- </div>
285
- </div>
286
- </div>
287
- </div>
288
- </transition>
289
- </header>
290
-
291
- <!-- Main Content -->
292
- <div class="flex flex-1 overflow-hidden relative p-5 transition-all duration-300 ease-in-out"
293
- :class="{ 'bg-slate-200/50': deviceMode !== 'auto' }">
294
- <div :style="containerStyle" class="transition-all duration-500 ease-in-out bg-slate-50 shadow-sm relative">
295
- <masonry v-model:items="items" :get-page="getPage" :load-at-page="1" :layout="layout"
296
- :layout-mode="deviceMode === 'phone' || deviceMode === 'tablet' ? 'swipe' : 'auto'" ref="masonry" init="auto"
297
- class="demo-masonry">
298
- <template #item-footer="{ item, remove }">
299
- <div class="h-full flex items-center justify-between px-3">
300
- <button v-if="remove"
301
- 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"
302
- @click.stop="remove(item)">
303
- Remove
304
- </button>
305
- <div class="text-[11px] text-slate-600">
306
- {{ item.width }}×{{ item.height }}
307
- </div>
308
- </div>
309
- </template>
310
- </masonry>
311
- </div>
312
- </div>
313
- </main>
314
- </template>
315
-
316
- <style scoped>
317
- /* Hover overlay for masonry items */
318
- .demo-masonry :deep(.masonry-item:hover .demo-item-overlay) {
319
- opacity: 1;
320
- }
321
- </style>
package/toggle-link.mjs DELETED
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { readFileSync, writeFileSync } from 'fs';
4
- import { resolve, dirname, relative } from 'path';
5
- import { fileURLToPath } from 'url';
6
- import { execSync } from 'child_process';
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
-
11
- // Get the vibe package directory
12
- const vibeDir = resolve(__dirname);
13
- const vibePackageJson = JSON.parse(readFileSync(resolve(vibeDir, 'package.json'), 'utf-8'));
14
- const vibeVersion = vibePackageJson.version;
15
-
16
- // Get the current working directory (where the script is run from)
17
- const cwd = process.cwd();
18
- const targetPackageJsonPath = resolve(cwd, 'package.json');
19
-
20
- try {
21
- const targetPackageJson = JSON.parse(readFileSync(targetPackageJsonPath, 'utf-8'));
22
-
23
- // Check if @wyxos/vibe exists in dependencies or devDependencies
24
- const deps = { ...targetPackageJson.dependencies, ...targetPackageJson.devDependencies };
25
- const currentVibeRef = deps['@wyxos/vibe'];
26
-
27
- if (!currentVibeRef) {
28
- console.error('❌ @wyxos/vibe not found in package.json dependencies or devDependencies');
29
- process.exit(1);
30
- }
31
-
32
- // Determine if it's a local path or version reference
33
- const isLocalPath = currentVibeRef.startsWith('file:') || currentVibeRef.startsWith('../') || currentVibeRef.startsWith('./');
34
-
35
- let newRef;
36
- let action;
37
-
38
- if (isLocalPath) {
39
- // Switch to published version
40
- newRef = `^${vibeVersion}`;
41
- action = 'published version';
42
- } else {
43
- // Switch to local path
44
- // Calculate relative path from target to vibe
45
- const relativePath = relative(cwd, vibeDir).replace(/\\/g, '/');
46
- newRef = `file:${relativePath}`;
47
- action = 'local path';
48
- }
49
-
50
- // Update package.json
51
- if (targetPackageJson.dependencies && targetPackageJson.dependencies['@wyxos/vibe']) {
52
- targetPackageJson.dependencies['@wyxos/vibe'] = newRef;
53
- }
54
- if (targetPackageJson.devDependencies && targetPackageJson.devDependencies['@wyxos/vibe']) {
55
- targetPackageJson.devDependencies['@wyxos/vibe'] = newRef;
56
- }
57
-
58
- // Write updated package.json
59
- writeFileSync(
60
- targetPackageJsonPath,
61
- JSON.stringify(targetPackageJson, null, 2) + '\n',
62
- 'utf-8'
63
- );
64
-
65
- console.log(`✅ Switched @wyxos/vibe to ${action}: ${newRef}`);
66
-
67
- // If switching to local path, ensure the library is built before installation
68
- if (action === 'local path') {
69
- try {
70
- console.log('🔧 Building local @wyxos/vibe (build:lib)...');
71
- execSync('npm run build:lib', { cwd: vibeDir, stdio: 'inherit' });
72
- } catch (e) {
73
- console.warn('⚠️ Failed to build local @wyxos/vibe. Attempting install anyway.');
74
- }
75
- }
76
-
77
- // Run npm install
78
- console.log('📦 Running npm install...');
79
- execSync('npm install', { cwd, stdio: 'inherit' });
80
-
81
- console.log('✨ Done!');
82
-
83
- } catch (error) {
84
- if (error.code === 'ENOENT') {
85
- console.error(`❌ package.json not found in ${cwd}`);
86
- console.error(' Make sure you run this script from a project directory that uses @wyxos/vibe');
87
- } else {
88
- console.error('❌ Error:', error.message);
89
- }
90
- process.exit(1);
91
- }
92
-