@wyxos/vibe 1.6.12 → 1.6.14
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/lib/index.js +778 -701
- package/lib/vibe.css +1 -1
- package/package.json +92 -67
- package/src/Masonry.vue +1233 -1213
- package/src/components/CodeTabs.vue +158 -0
- package/src/components/MasonryItem.vue +431 -346
- package/src/components/examples/BasicExample.vue +45 -0
- package/src/components/examples/CustomItemExample.vue +86 -0
- package/src/components/examples/HeaderFooterExample.vue +78 -0
- package/src/components/examples/SwipeModeExample.vue +39 -0
- package/src/pages.json +36401 -36401
- package/src/useMasonryScroll.ts +60 -60
- package/src/views/Examples.vue +275 -20
- package/src/views/Home.vue +236 -208
- package/toggle-link.mjs +92 -92
package/src/views/Home.vue
CHANGED
|
@@ -1,208 +1,236 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<main class="flex flex-col h-screen overflow-hidden bg-slate-50 relative pt-[112px]">
|
|
3
|
-
<!-- Fixed Sub-Header -->
|
|
4
|
-
<header 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">
|
|
5
|
-
<div class="max-w-7xl mx-auto px-4 h-14 flex items-center justify-end">
|
|
6
|
-
<!-- Right: Controls -->
|
|
7
|
-
<div class="flex items-center gap-3">
|
|
8
|
-
<!-- Status Pill -->
|
|
9
|
-
<div v-if="masonry" 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">
|
|
10
|
-
<span class="flex items-center gap-1.5">
|
|
11
|
-
<span class="w-1.5 h-1.5 rounded-full shadow-sm" :class="masonry.isLoading ? 'bg-amber-400 animate-pulse' : 'bg-emerald-400'"></span>
|
|
12
|
-
{{ masonry.isLoading ? 'Loading...' : 'Ready' }}
|
|
13
|
-
</span>
|
|
14
|
-
<span class="w-px h-3 bg-slate-200"></span>
|
|
15
|
-
<span>{{ items.length }} items</span>
|
|
16
|
-
</div>
|
|
17
|
-
|
|
18
|
-
<div class="h-8 w-px bg-slate-100 mx-1 hidden md:block"></div>
|
|
19
|
-
|
|
20
|
-
<!-- Action Buttons -->
|
|
21
|
-
<div class="flex items-center gap-1">
|
|
22
|
-
<button
|
|
23
|
-
@click="showLayoutControls = !showLayoutControls"
|
|
24
|
-
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"
|
|
25
|
-
:class="{ 'text-blue-600 bg-blue-50 ring-2 ring-blue-100': showLayoutControls }"
|
|
26
|
-
title="Layout Settings"
|
|
27
|
-
>
|
|
28
|
-
<i class="fas fa-sliders text-sm"></i>
|
|
29
|
-
</button>
|
|
30
|
-
|
|
31
|
-
<a
|
|
32
|
-
href="https://github.com/wyxos/vibe"
|
|
33
|
-
target="_blank"
|
|
34
|
-
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"
|
|
35
|
-
title="View on GitHub"
|
|
36
|
-
>
|
|
37
|
-
<i class="fab fa-github text-lg"></i>
|
|
38
|
-
</a>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<!-- Layout Controls Panel -->
|
|
44
|
-
<transition
|
|
45
|
-
enter-active-class="transition duration-200 ease-out"
|
|
46
|
-
enter-from-class="transform -translate-y-2 opacity-0"
|
|
47
|
-
enter-to-class="transform translate-y-0 opacity-100"
|
|
48
|
-
leave-active-class="transition duration-150 ease-in"
|
|
49
|
-
leave-from-class="transform translate-y-0 opacity-100"
|
|
50
|
-
leave-to-class="transform -translate-y-2 opacity-0"
|
|
51
|
-
>
|
|
52
|
-
<div v-if="showLayoutControls" 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">
|
|
53
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
|
|
54
|
-
<!-- Column Settings -->
|
|
55
|
-
<div>
|
|
56
|
-
<h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Column Configuration</h3>
|
|
57
|
-
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 sm:gap-3">
|
|
58
|
-
<div v-for="(val, key) in layoutParams.sizes" :key="key" class="flex flex-col gap-1.5">
|
|
59
|
-
<label class="text-[10px] font-bold text-slate-500 uppercase text-center">{{ key }}</label>
|
|
60
|
-
<input
|
|
61
|
-
v-model.number="layoutParams.sizes[key]"
|
|
62
|
-
type="number"
|
|
63
|
-
min="1"
|
|
64
|
-
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"
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
<!-- Spacing Settings -->
|
|
71
|
-
<div>
|
|
72
|
-
<h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Spacing</h3>
|
|
73
|
-
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
74
|
-
<div class="flex flex-col gap-1.5">
|
|
75
|
-
<label class="text-[10px] font-bold text-slate-500 uppercase">Header Offset</label>
|
|
76
|
-
<div class="relative">
|
|
77
|
-
<input
|
|
78
|
-
v-model.number="layoutParams.header"
|
|
79
|
-
type="number"
|
|
80
|
-
min="0"
|
|
81
|
-
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"
|
|
82
|
-
/>
|
|
83
|
-
<span class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-400">px</span>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
<div class="flex flex-col gap-1.5">
|
|
87
|
-
<label class="text-[10px] font-bold text-slate-500 uppercase">Footer Offset</label>
|
|
88
|
-
<div class="relative">
|
|
89
|
-
<input
|
|
90
|
-
v-model.number="layoutParams.footer"
|
|
91
|
-
type="number"
|
|
92
|
-
min="0"
|
|
93
|
-
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"
|
|
94
|
-
/>
|
|
95
|
-
<span class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-400">px</span>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
<!-- Device Simulation -->
|
|
102
|
-
<div class="md:col-span-2 border-t border-slate-100 pt-6 mt-2">
|
|
103
|
-
<h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Device Simulation</h3>
|
|
104
|
-
<div class="flex flex-wrap gap-2">
|
|
105
|
-
<button
|
|
106
|
-
v-for="mode in ['auto', 'phone', 'tablet', 'desktop']"
|
|
107
|
-
:key="mode"
|
|
108
|
-
@click="deviceMode = mode as any"
|
|
109
|
-
class="px-4 py-2 rounded-lg text-sm font-medium transition-all capitalize"
|
|
110
|
-
:class="deviceMode === mode ? 'bg-blue-600 text-white shadow-md shadow-blue-200' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'"
|
|
111
|
-
>
|
|
112
|
-
<i class="fas mr-2" :class="{
|
|
113
|
-
'fa-desktop': mode === 'desktop' || mode === 'auto',
|
|
114
|
-
'fa-mobile-alt': mode === 'phone',
|
|
115
|
-
'fa-tablet-alt': mode === 'tablet'
|
|
116
|
-
}"></i>
|
|
117
|
-
{{ mode }}
|
|
118
|
-
</button>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
</transition>
|
|
124
|
-
</header>
|
|
125
|
-
|
|
126
|
-
<!-- Main Content -->
|
|
127
|
-
<div class="flex flex-1 overflow-hidden relative p-5 transition-all duration-300 ease-in-out" :class="{'bg-slate-200/50': deviceMode !== 'auto'}">
|
|
128
|
-
<div :style="containerStyle" class="transition-all duration-500 ease-in-out bg-slate-50 shadow-sm relative">
|
|
129
|
-
<masonry v-model:items="items" :get-next-page="getPage" :load-at-page="1" :layout="layout" ref="masonry">
|
|
130
|
-
<!--
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
});
|
|
208
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<main class="flex flex-col h-screen overflow-hidden bg-slate-50 relative pt-[112px]">
|
|
3
|
+
<!-- Fixed Sub-Header -->
|
|
4
|
+
<header 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">
|
|
5
|
+
<div class="max-w-7xl mx-auto px-4 h-14 flex items-center justify-end">
|
|
6
|
+
<!-- Right: Controls -->
|
|
7
|
+
<div class="flex items-center gap-3">
|
|
8
|
+
<!-- Status Pill -->
|
|
9
|
+
<div v-if="masonry" 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">
|
|
10
|
+
<span class="flex items-center gap-1.5">
|
|
11
|
+
<span class="w-1.5 h-1.5 rounded-full shadow-sm" :class="masonry.isLoading ? 'bg-amber-400 animate-pulse' : 'bg-emerald-400'"></span>
|
|
12
|
+
{{ masonry.isLoading ? 'Loading...' : 'Ready' }}
|
|
13
|
+
</span>
|
|
14
|
+
<span class="w-px h-3 bg-slate-200"></span>
|
|
15
|
+
<span>{{ items.length }} items</span>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="h-8 w-px bg-slate-100 mx-1 hidden md:block"></div>
|
|
19
|
+
|
|
20
|
+
<!-- Action Buttons -->
|
|
21
|
+
<div class="flex items-center gap-1">
|
|
22
|
+
<button
|
|
23
|
+
@click="showLayoutControls = !showLayoutControls"
|
|
24
|
+
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"
|
|
25
|
+
:class="{ 'text-blue-600 bg-blue-50 ring-2 ring-blue-100': showLayoutControls }"
|
|
26
|
+
title="Layout Settings"
|
|
27
|
+
>
|
|
28
|
+
<i class="fas fa-sliders text-sm"></i>
|
|
29
|
+
</button>
|
|
30
|
+
|
|
31
|
+
<a
|
|
32
|
+
href="https://github.com/wyxos/vibe"
|
|
33
|
+
target="_blank"
|
|
34
|
+
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"
|
|
35
|
+
title="View on GitHub"
|
|
36
|
+
>
|
|
37
|
+
<i class="fab fa-github text-lg"></i>
|
|
38
|
+
</a>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<!-- Layout Controls Panel -->
|
|
44
|
+
<transition
|
|
45
|
+
enter-active-class="transition duration-200 ease-out"
|
|
46
|
+
enter-from-class="transform -translate-y-2 opacity-0"
|
|
47
|
+
enter-to-class="transform translate-y-0 opacity-100"
|
|
48
|
+
leave-active-class="transition duration-150 ease-in"
|
|
49
|
+
leave-from-class="transform translate-y-0 opacity-100"
|
|
50
|
+
leave-to-class="transform -translate-y-2 opacity-0"
|
|
51
|
+
>
|
|
52
|
+
<div v-if="showLayoutControls" 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">
|
|
53
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
|
|
54
|
+
<!-- Column Settings -->
|
|
55
|
+
<div>
|
|
56
|
+
<h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Column Configuration</h3>
|
|
57
|
+
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 sm:gap-3">
|
|
58
|
+
<div v-for="(val, key) in layoutParams.sizes" :key="key" class="flex flex-col gap-1.5">
|
|
59
|
+
<label class="text-[10px] font-bold text-slate-500 uppercase text-center">{{ key }}</label>
|
|
60
|
+
<input
|
|
61
|
+
v-model.number="layoutParams.sizes[key]"
|
|
62
|
+
type="number"
|
|
63
|
+
min="1"
|
|
64
|
+
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"
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<!-- Spacing Settings -->
|
|
71
|
+
<div>
|
|
72
|
+
<h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Spacing</h3>
|
|
73
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
74
|
+
<div class="flex flex-col gap-1.5">
|
|
75
|
+
<label class="text-[10px] font-bold text-slate-500 uppercase">Header Offset</label>
|
|
76
|
+
<div class="relative">
|
|
77
|
+
<input
|
|
78
|
+
v-model.number="layoutParams.header"
|
|
79
|
+
type="number"
|
|
80
|
+
min="0"
|
|
81
|
+
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"
|
|
82
|
+
/>
|
|
83
|
+
<span class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-400">px</span>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="flex flex-col gap-1.5">
|
|
87
|
+
<label class="text-[10px] font-bold text-slate-500 uppercase">Footer Offset</label>
|
|
88
|
+
<div class="relative">
|
|
89
|
+
<input
|
|
90
|
+
v-model.number="layoutParams.footer"
|
|
91
|
+
type="number"
|
|
92
|
+
min="0"
|
|
93
|
+
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"
|
|
94
|
+
/>
|
|
95
|
+
<span class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-slate-400">px</span>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<!-- Device Simulation -->
|
|
102
|
+
<div class="md:col-span-2 border-t border-slate-100 pt-6 mt-2">
|
|
103
|
+
<h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Device Simulation</h3>
|
|
104
|
+
<div class="flex flex-wrap gap-2">
|
|
105
|
+
<button
|
|
106
|
+
v-for="mode in ['auto', 'phone', 'tablet', 'desktop']"
|
|
107
|
+
:key="mode"
|
|
108
|
+
@click="deviceMode = mode as any"
|
|
109
|
+
class="px-4 py-2 rounded-lg text-sm font-medium transition-all capitalize"
|
|
110
|
+
:class="deviceMode === mode ? 'bg-blue-600 text-white shadow-md shadow-blue-200' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'"
|
|
111
|
+
>
|
|
112
|
+
<i class="fas mr-2" :class="{
|
|
113
|
+
'fa-desktop': mode === 'desktop' || mode === 'auto',
|
|
114
|
+
'fa-mobile-alt': mode === 'phone',
|
|
115
|
+
'fa-tablet-alt': mode === 'tablet'
|
|
116
|
+
}"></i>
|
|
117
|
+
{{ mode }}
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</transition>
|
|
124
|
+
</header>
|
|
125
|
+
|
|
126
|
+
<!-- Main Content -->
|
|
127
|
+
<div class="flex flex-1 overflow-hidden relative p-5 transition-all duration-300 ease-in-out" :class="{'bg-slate-200/50': deviceMode !== 'auto'}">
|
|
128
|
+
<div :style="containerStyle" class="transition-all duration-500 ease-in-out bg-slate-50 shadow-sm relative">
|
|
129
|
+
<masonry v-model:items="items" :get-next-page="getPage" :load-at-page="1" :layout="layout" ref="masonry">
|
|
130
|
+
<!-- Demonstrate header/footer customization in the main demo -->
|
|
131
|
+
<template #item-header="{ item }">
|
|
132
|
+
<div class="h-full flex items-center justify-between px-3">
|
|
133
|
+
<div class="flex items-center gap-2">
|
|
134
|
+
<div class="w-6 h-6 rounded-full bg-white/80 backdrop-blur-sm flex items-center justify-center shadow-sm">
|
|
135
|
+
<i :class="item.type === 'video' ? 'fas fa-video text-[10px] text-slate-500' : 'fas fa-image text-[10px] text-slate-500'"></i>
|
|
136
|
+
</div>
|
|
137
|
+
<span class="text-xs font-medium text-slate-700">#{{ String(item.id).split('-')[0] }}</span>
|
|
138
|
+
</div>
|
|
139
|
+
<span v-if="item.title" class="text-[11px] text-slate-600 truncate max-w-[160px]">
|
|
140
|
+
{{ item.title }}
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
143
|
+
</template>
|
|
144
|
+
|
|
145
|
+
<template #item-footer="{ item, remove }">
|
|
146
|
+
<div class="h-full flex items-center justify-between px-3">
|
|
147
|
+
<button
|
|
148
|
+
v-if="remove"
|
|
149
|
+
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"
|
|
150
|
+
@click.stop="remove(item)"
|
|
151
|
+
>
|
|
152
|
+
Remove
|
|
153
|
+
</button>
|
|
154
|
+
<div class="text-[11px] text-slate-600">
|
|
155
|
+
{{ item.width }}×{{ item.height }}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</template>
|
|
159
|
+
</masonry>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</main>
|
|
163
|
+
</template>
|
|
164
|
+
|
|
165
|
+
<script setup lang="ts">
|
|
166
|
+
import Masonry from "../Masonry.vue";
|
|
167
|
+
import { ref, reactive, computed } from "vue";
|
|
168
|
+
import fixture from "../pages.json";
|
|
169
|
+
import type { MasonryItem, GetPageResult } from "../types";
|
|
170
|
+
|
|
171
|
+
const items = ref<MasonryItem[]>([]);
|
|
172
|
+
|
|
173
|
+
const masonry = ref<InstanceType<typeof Masonry> | null>(null);
|
|
174
|
+
|
|
175
|
+
const layoutParams = reactive({
|
|
176
|
+
sizes: {
|
|
177
|
+
base: 1,
|
|
178
|
+
sm: 2,
|
|
179
|
+
md: 3,
|
|
180
|
+
lg: 4,
|
|
181
|
+
xl: 5,
|
|
182
|
+
'2xl': 10
|
|
183
|
+
},
|
|
184
|
+
header: 36,
|
|
185
|
+
footer: 40
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const layout = computed(() => ({
|
|
189
|
+
sizes: { ...layoutParams.sizes },
|
|
190
|
+
header: layoutParams.header,
|
|
191
|
+
footer: layoutParams.footer
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
const showLayoutControls = ref(false);
|
|
195
|
+
|
|
196
|
+
const getPage = async (page: number): Promise<GetPageResult> => {
|
|
197
|
+
return new Promise((resolve) => {
|
|
198
|
+
setTimeout(() => {
|
|
199
|
+
// Check if the page exists in the fixture
|
|
200
|
+
const pageData = (fixture as any[])[page - 1] as { items: MasonryItem[] } | undefined;
|
|
201
|
+
|
|
202
|
+
if (!pageData) {
|
|
203
|
+
// Return empty items if page doesn't exist
|
|
204
|
+
resolve({
|
|
205
|
+
items: [],
|
|
206
|
+
nextPage: null // null indicates no more pages
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const output: GetPageResult = {
|
|
212
|
+
items: pageData.items,
|
|
213
|
+
nextPage: page < (fixture as any[]).length ? page + 1 : null
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
resolve(output);
|
|
217
|
+
}, 1000);
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Device Simulation
|
|
222
|
+
const deviceMode = ref<'auto' | 'phone' | 'tablet' | 'desktop'>('auto');
|
|
223
|
+
|
|
224
|
+
const containerStyle = computed(() => {
|
|
225
|
+
switch (deviceMode.value) {
|
|
226
|
+
case 'phone':
|
|
227
|
+
return { width: '375px', maxWidth: '100%', margin: '0 auto', border: '1px solid #e2e8f0', borderRadius: '20px', overflow: 'hidden', height: '100%' };
|
|
228
|
+
case 'tablet':
|
|
229
|
+
return { width: '768px', maxWidth: '100%', margin: '0 auto', border: '1px solid #e2e8f0', borderRadius: '12px', overflow: 'hidden', height: '100%' };
|
|
230
|
+
case 'desktop':
|
|
231
|
+
return { width: '1280px', maxWidth: '100%', margin: '0 auto', border: '1px solid #e2e8f0', borderRadius: '8px', overflow: 'hidden', height: '100%' };
|
|
232
|
+
default:
|
|
233
|
+
return { width: '100%', height: '100%' };
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
</script>
|
package/toggle-link.mjs
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
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
|
-
|
|
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
|
+
|