@getmicdrop/svelte-components 5.0.0 → 5.1.1
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/dist/primitives/Button/Button.stories.svelte +46 -79
- package/dist/primitives/Button/Button.stories.svelte.d.ts.map +1 -1
- package/dist/primitives/Button/Button.svelte +11 -5
- package/dist/primitives/Button/Button.svelte.d.ts +2 -0
- package/dist/primitives/Button/Button.svelte.d.ts.map +1 -1
- package/dist/primitives/Button/ButtonVariantShowcase.svelte +137 -0
- package/dist/primitives/Button/ButtonVariantShowcase.svelte.d.ts +27 -0
- package/dist/primitives/Button/ButtonVariantShowcase.svelte.d.ts.map +1 -0
- package/dist/recipes/SuperLogin/SuperLogin.svelte +5 -5
- package/dist/stories/ButtonAuditDashboard.stories.svelte +14 -0
- package/dist/stories/ButtonAuditDashboard.stories.svelte.d.ts +28 -0
- package/dist/stories/ButtonAuditDashboard.stories.svelte.d.ts.map +1 -0
- package/dist/stories/ButtonAuditDashboard.svelte +444 -0
- package/dist/stories/ButtonAuditDashboard.svelte.d.ts +7 -0
- package/dist/stories/ButtonAuditDashboard.svelte.d.ts.map +1 -0
- package/dist/stories/ButtonAuditReview.stories.svelte +14 -0
- package/dist/stories/ButtonAuditReview.stories.svelte.d.ts +28 -0
- package/dist/stories/ButtonAuditReview.stories.svelte.d.ts.map +1 -0
- package/dist/stories/ButtonAuditReview.svelte +463 -0
- package/dist/stories/ButtonAuditReview.svelte.d.ts +7 -0
- package/dist/stories/ButtonAuditReview.svelte.d.ts.map +1 -0
- package/dist/stories/ButtonGridView.stories.svelte +14 -0
- package/dist/stories/ButtonGridView.stories.svelte.d.ts +28 -0
- package/dist/stories/ButtonGridView.stories.svelte.d.ts.map +1 -0
- package/dist/stories/ButtonGridView.svelte +146 -0
- package/dist/stories/ButtonGridView.svelte.d.ts +7 -0
- package/dist/stories/ButtonGridView.svelte.d.ts.map +1 -0
- package/dist/stories/ButtonShowcase.stories.svelte +14 -0
- package/dist/stories/ButtonShowcase.stories.svelte.d.ts +28 -0
- package/dist/stories/ButtonShowcase.stories.svelte.d.ts.map +1 -0
- package/dist/stories/ButtonShowcase.svelte +529 -0
- package/dist/stories/ButtonShowcase.svelte.d.ts +7 -0
- package/dist/stories/ButtonShowcase.svelte.d.ts.map +1 -0
- package/dist/stories/DesignSystemAudit.stories.svelte +4 -12
- package/dist/stories/DesignSystemAudit.stories.svelte.d.ts.map +1 -1
- package/dist/stories/button-audit-manifest.json +11187 -0
- package/package.json +5 -2
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Button from '../primitives/Button/Button.svelte';
|
|
3
|
+
import manifest from './button-audit-manifest.json';
|
|
4
|
+
|
|
5
|
+
// Config - adjust these for your setup
|
|
6
|
+
const FRONTEND_DEV_URL = 'http://localhost:5173';
|
|
7
|
+
|
|
8
|
+
// Dark mode toggle
|
|
9
|
+
let isDark = $state(false);
|
|
10
|
+
|
|
11
|
+
function toggleDarkMode() {
|
|
12
|
+
isDark = !isDark;
|
|
13
|
+
if (isDark) {
|
|
14
|
+
document.documentElement.classList.add('dark');
|
|
15
|
+
} else {
|
|
16
|
+
document.documentElement.classList.remove('dark');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Preview modal state
|
|
21
|
+
let previewModal = $state({ open: false, url: '', title: '', file: '' });
|
|
22
|
+
|
|
23
|
+
function openPreview(route, file) {
|
|
24
|
+
// Convert file path to URL route
|
|
25
|
+
// routes/admin/venues/+page.svelte -> /admin/venues
|
|
26
|
+
// routes/login/+page.svelte -> /login
|
|
27
|
+
let urlPath = file
|
|
28
|
+
.replace(/^routes/, '')
|
|
29
|
+
.replace(/\/?\+[^/]+\.svelte$/, '')
|
|
30
|
+
.replace(/\[([^\]]+)\]/g, ':$1'); // Convert [param] to :param for display
|
|
31
|
+
|
|
32
|
+
if (!urlPath || urlPath === '/') urlPath = '/';
|
|
33
|
+
|
|
34
|
+
previewModal = {
|
|
35
|
+
open: true,
|
|
36
|
+
url: `${FRONTEND_DEV_URL}${urlPath}`,
|
|
37
|
+
title: route,
|
|
38
|
+
file: file
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function closePreview() {
|
|
43
|
+
previewModal = { open: false, url: '', title: '', file: '' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check if a file is previewable (only route pages, not components)
|
|
47
|
+
function isPreviewable(file) {
|
|
48
|
+
return file.startsWith('routes/') && file.includes('+page');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Collapsible state for categories
|
|
52
|
+
let expandedCategories = $state(new Set());
|
|
53
|
+
let expandedFiles = $state(new Set());
|
|
54
|
+
|
|
55
|
+
function toggleCategory(category) {
|
|
56
|
+
const newSet = new Set(expandedCategories);
|
|
57
|
+
if (newSet.has(category)) {
|
|
58
|
+
newSet.delete(category);
|
|
59
|
+
} else {
|
|
60
|
+
newSet.add(category);
|
|
61
|
+
}
|
|
62
|
+
expandedCategories = newSet;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function toggleFile(fileKey) {
|
|
66
|
+
const newSet = new Set(expandedFiles);
|
|
67
|
+
if (newSet.has(fileKey)) {
|
|
68
|
+
newSet.delete(fileKey);
|
|
69
|
+
} else {
|
|
70
|
+
newSet.add(fileKey);
|
|
71
|
+
}
|
|
72
|
+
expandedFiles = newSet;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function expandAll() {
|
|
76
|
+
expandedCategories = new Set(Object.keys(manifest.categories));
|
|
77
|
+
const allFiles = Object.values(manifest.categories).flatMap(routes =>
|
|
78
|
+
routes.map(r => r.fullPath)
|
|
79
|
+
);
|
|
80
|
+
expandedFiles = new Set(allFiles);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function collapseAll() {
|
|
84
|
+
expandedCategories = new Set();
|
|
85
|
+
expandedFiles = new Set();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Generate VS Code link
|
|
89
|
+
function getVSCodeLink(fullPath, line) {
|
|
90
|
+
return `vscode://file${fullPath}:${line}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Get variant badge color
|
|
94
|
+
function getVariantColor(variant) {
|
|
95
|
+
switch (variant) {
|
|
96
|
+
case 'blue-solid': return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200';
|
|
97
|
+
case 'gray-outline': return 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200';
|
|
98
|
+
case 'blue-outline': return 'bg-blue-50 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300';
|
|
99
|
+
case 'gray-text': return 'bg-gray-50 text-gray-600 dark:bg-gray-800 dark:text-gray-400';
|
|
100
|
+
case 'red-solid': return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200';
|
|
101
|
+
case 'red-outline': return 'bg-red-50 text-red-700 dark:bg-red-900/50 dark:text-red-300';
|
|
102
|
+
case 'green-solid': return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200';
|
|
103
|
+
case 'icon': return 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200';
|
|
104
|
+
case 'ghost': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200';
|
|
105
|
+
default: return 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Filter state
|
|
110
|
+
let variantFilter = $state('all');
|
|
111
|
+
let sizeFilter = $state('all');
|
|
112
|
+
let searchQuery = $state('');
|
|
113
|
+
|
|
114
|
+
// Get all unique variants and sizes
|
|
115
|
+
const allVariants = [...new Set(
|
|
116
|
+
Object.values(manifest.categories)
|
|
117
|
+
.flatMap(routes => routes.flatMap(r => r.buttons.map(b => b.variant)))
|
|
118
|
+
)].sort();
|
|
119
|
+
|
|
120
|
+
const allSizes = [...new Set(
|
|
121
|
+
Object.values(manifest.categories)
|
|
122
|
+
.flatMap(routes => routes.flatMap(r => r.buttons.map(b => b.size)))
|
|
123
|
+
)].sort();
|
|
124
|
+
|
|
125
|
+
// Filter function
|
|
126
|
+
function shouldShowButton(button) {
|
|
127
|
+
if (variantFilter !== 'all' && button.variant !== variantFilter) return false;
|
|
128
|
+
if (sizeFilter !== 'all' && button.size !== sizeFilter) return false;
|
|
129
|
+
if (searchQuery) {
|
|
130
|
+
const query = searchQuery.toLowerCase();
|
|
131
|
+
return button.text.toLowerCase().includes(query) ||
|
|
132
|
+
button.variant.toLowerCase().includes(query);
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Computed filtered stats
|
|
138
|
+
let filteredStats = $derived.by(() => {
|
|
139
|
+
let total = 0;
|
|
140
|
+
let shown = 0;
|
|
141
|
+
for (const routes of Object.values(manifest.categories)) {
|
|
142
|
+
for (const route of routes) {
|
|
143
|
+
for (const button of route.buttons) {
|
|
144
|
+
total++;
|
|
145
|
+
if (shouldShowButton(button)) shown++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { total, shown };
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Sort categories by button count
|
|
153
|
+
const sortedCategories = Object.entries(manifest.categories)
|
|
154
|
+
.map(([name, routes]) => ({
|
|
155
|
+
name,
|
|
156
|
+
routes,
|
|
157
|
+
buttonCount: routes.reduce((sum, r) => sum + r.buttons.length, 0)
|
|
158
|
+
}))
|
|
159
|
+
.sort((a, b) => b.buttonCount - a.buttonCount);
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors">
|
|
163
|
+
<!-- Sticky Header -->
|
|
164
|
+
<div class="sticky top-0 z-50 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 shadow-sm">
|
|
165
|
+
<div class="max-w-7xl mx-auto px-4 py-4">
|
|
166
|
+
<div class="flex flex-wrap items-center justify-between gap-4">
|
|
167
|
+
<div>
|
|
168
|
+
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Button Audit Dashboard</h1>
|
|
169
|
+
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
170
|
+
{filteredStats.shown} of {filteredStats.total} buttons from micdrop-frontend
|
|
171
|
+
<span class="text-xs ml-2">Generated: {new Date(manifest.generated).toLocaleString()}</span>
|
|
172
|
+
</p>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div class="flex items-center gap-3">
|
|
176
|
+
<!-- Expand/Collapse All -->
|
|
177
|
+
<button
|
|
178
|
+
onclick={expandAll}
|
|
179
|
+
class="px-3 py-1.5 text-sm rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
|
|
180
|
+
>
|
|
181
|
+
Expand All
|
|
182
|
+
</button>
|
|
183
|
+
<button
|
|
184
|
+
onclick={collapseAll}
|
|
185
|
+
class="px-3 py-1.5 text-sm rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
|
|
186
|
+
>
|
|
187
|
+
Collapse All
|
|
188
|
+
</button>
|
|
189
|
+
|
|
190
|
+
<!-- Dark mode toggle -->
|
|
191
|
+
<button
|
|
192
|
+
onclick={toggleDarkMode}
|
|
193
|
+
class="px-4 py-2 rounded-lg font-medium transition-all {isDark
|
|
194
|
+
? 'bg-yellow-500 text-black hover:bg-yellow-400'
|
|
195
|
+
: 'bg-gray-800 text-white hover:bg-gray-700'}"
|
|
196
|
+
>
|
|
197
|
+
{#if isDark}
|
|
198
|
+
☀️ Light
|
|
199
|
+
{:else}
|
|
200
|
+
🌙 Dark
|
|
201
|
+
{/if}
|
|
202
|
+
</button>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<!-- Filters Row -->
|
|
207
|
+
<div class="flex flex-wrap items-center gap-4 mt-4">
|
|
208
|
+
<!-- Search -->
|
|
209
|
+
<div class="flex-1 min-w-48">
|
|
210
|
+
<input
|
|
211
|
+
type="text"
|
|
212
|
+
bind:value={searchQuery}
|
|
213
|
+
placeholder="Search button text..."
|
|
214
|
+
class="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400"
|
|
215
|
+
/>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<!-- Variant Filter -->
|
|
219
|
+
<select
|
|
220
|
+
bind:value={variantFilter}
|
|
221
|
+
class="px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
222
|
+
>
|
|
223
|
+
<option value="all">All Variants</option>
|
|
224
|
+
{#each allVariants as variant}
|
|
225
|
+
<option value={variant}>{variant}</option>
|
|
226
|
+
{/each}
|
|
227
|
+
</select>
|
|
228
|
+
|
|
229
|
+
<!-- Size Filter -->
|
|
230
|
+
<select
|
|
231
|
+
bind:value={sizeFilter}
|
|
232
|
+
class="px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
|
|
233
|
+
>
|
|
234
|
+
<option value="all">All Sizes</option>
|
|
235
|
+
{#each allSizes as size}
|
|
236
|
+
<option value={size}>{size}</option>
|
|
237
|
+
{/each}
|
|
238
|
+
</select>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<!-- Main Content -->
|
|
244
|
+
<div class="max-w-7xl mx-auto px-4 py-6 space-y-4">
|
|
245
|
+
{#each sortedCategories as category}
|
|
246
|
+
{@const visibleButtons = category.routes
|
|
247
|
+
.flatMap(r => r.buttons)
|
|
248
|
+
.filter(shouldShowButton)}
|
|
249
|
+
|
|
250
|
+
{#if visibleButtons.length > 0}
|
|
251
|
+
<!-- Category Section -->
|
|
252
|
+
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden">
|
|
253
|
+
<!-- Category Header -->
|
|
254
|
+
<button
|
|
255
|
+
onclick={() => toggleCategory(category.name)}
|
|
256
|
+
class="w-full flex items-center justify-between px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"
|
|
257
|
+
>
|
|
258
|
+
<div class="flex items-center gap-3">
|
|
259
|
+
<span class="text-xl">{expandedCategories.has(category.name) ? '▼' : '▶'}</span>
|
|
260
|
+
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">{category.name}</h2>
|
|
261
|
+
<span class="px-2 py-0.5 text-xs font-medium rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
|
|
262
|
+
{visibleButtons.length} buttons
|
|
263
|
+
</span>
|
|
264
|
+
<span class="text-sm text-gray-500 dark:text-gray-400">
|
|
265
|
+
in {category.routes.length} files
|
|
266
|
+
</span>
|
|
267
|
+
</div>
|
|
268
|
+
</button>
|
|
269
|
+
|
|
270
|
+
<!-- Category Content -->
|
|
271
|
+
{#if expandedCategories.has(category.name)}
|
|
272
|
+
<div class="border-t border-gray-200 dark:border-gray-700">
|
|
273
|
+
{#each category.routes as route}
|
|
274
|
+
{@const routeVisibleButtons = route.buttons.filter(shouldShowButton)}
|
|
275
|
+
|
|
276
|
+
{#if routeVisibleButtons.length > 0}
|
|
277
|
+
<!-- File Section -->
|
|
278
|
+
<div class="border-b border-gray-100 dark:border-gray-700/50 last:border-b-0">
|
|
279
|
+
<button
|
|
280
|
+
onclick={() => toggleFile(route.fullPath)}
|
|
281
|
+
class="w-full flex items-center justify-between px-6 py-2 hover:bg-gray-50 dark:hover:bg-gray-700/30 transition-colors text-left"
|
|
282
|
+
>
|
|
283
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
284
|
+
<span class="text-gray-400">{expandedFiles.has(route.fullPath) ? '▾' : '▸'}</span>
|
|
285
|
+
<code class="text-sm text-gray-600 dark:text-gray-300 truncate">{route.file}</code>
|
|
286
|
+
<a
|
|
287
|
+
href={getVSCodeLink(route.fullPath, 1)}
|
|
288
|
+
onclick={(e) => e.stopPropagation()}
|
|
289
|
+
class="text-xs text-blue-500 hover:text-blue-700 dark:text-blue-400 hover:underline"
|
|
290
|
+
title="Open in VS Code"
|
|
291
|
+
>
|
|
292
|
+
📂 Open
|
|
293
|
+
</a>
|
|
294
|
+
</div>
|
|
295
|
+
<div class="flex items-center gap-2 flex-shrink-0 ml-2">
|
|
296
|
+
{#if isPreviewable(route.file)}
|
|
297
|
+
<button
|
|
298
|
+
onclick={(e) => { e.stopPropagation(); openPreview(route.route, route.file); }}
|
|
299
|
+
class="px-2 py-0.5 text-xs rounded bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900 dark:text-green-300 dark:hover:bg-green-800"
|
|
300
|
+
title="Preview this page"
|
|
301
|
+
>
|
|
302
|
+
👁️ Preview
|
|
303
|
+
</button>
|
|
304
|
+
{/if}
|
|
305
|
+
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
306
|
+
{routeVisibleButtons.length} buttons
|
|
307
|
+
</span>
|
|
308
|
+
</div>
|
|
309
|
+
</button>
|
|
310
|
+
|
|
311
|
+
<!-- Buttons Grid -->
|
|
312
|
+
{#if expandedFiles.has(route.fullPath)}
|
|
313
|
+
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900/50">
|
|
314
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
315
|
+
{#each routeVisibleButtons as button, idx}
|
|
316
|
+
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-600 space-y-3">
|
|
317
|
+
<!-- Button Preview -->
|
|
318
|
+
<div class="flex items-center justify-center p-4 bg-gray-100 dark:bg-gray-700 rounded-lg min-h-[60px]">
|
|
319
|
+
<Button
|
|
320
|
+
variant={button.variant}
|
|
321
|
+
size={button.size}
|
|
322
|
+
loading={button.loading}
|
|
323
|
+
disabled={button.disabled}
|
|
324
|
+
danger={button.danger}
|
|
325
|
+
deemphasized={button.deemphasized}
|
|
326
|
+
>
|
|
327
|
+
{button.text === '(no text)' ? 'Button' : button.text.slice(0, 30)}{button.text.length > 30 ? '...' : ''}
|
|
328
|
+
</Button>
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
<!-- Metadata -->
|
|
332
|
+
<div class="space-y-2">
|
|
333
|
+
<div class="flex flex-wrap gap-1">
|
|
334
|
+
<span class="px-2 py-0.5 text-xs font-medium rounded {getVariantColor(button.variant)}">
|
|
335
|
+
{button.variant}
|
|
336
|
+
</span>
|
|
337
|
+
<span class="px-2 py-0.5 text-xs font-medium rounded bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
|
|
338
|
+
{button.size}
|
|
339
|
+
</span>
|
|
340
|
+
{#if button.loading}
|
|
341
|
+
<span class="px-2 py-0.5 text-xs font-medium rounded bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-200">loading</span>
|
|
342
|
+
{/if}
|
|
343
|
+
{#if button.disabled}
|
|
344
|
+
<span class="px-2 py-0.5 text-xs font-medium rounded bg-gray-200 text-gray-600 dark:bg-gray-600 dark:text-gray-300">disabled</span>
|
|
345
|
+
{/if}
|
|
346
|
+
{#if button.danger}
|
|
347
|
+
<span class="px-2 py-0.5 text-xs font-medium rounded bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-200">danger</span>
|
|
348
|
+
{/if}
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
<div class="flex items-center justify-between text-xs">
|
|
352
|
+
<span class="text-gray-500 dark:text-gray-400">Line {button.line}</span>
|
|
353
|
+
<a
|
|
354
|
+
href={getVSCodeLink(route.fullPath, button.line)}
|
|
355
|
+
class="text-blue-500 hover:text-blue-700 dark:text-blue-400 hover:underline"
|
|
356
|
+
>
|
|
357
|
+
Open in Code →
|
|
358
|
+
</a>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
{#if button.text !== '(no text)' && button.text.length > 0}
|
|
362
|
+
<p class="text-xs text-gray-600 dark:text-gray-400 truncate" title={button.text}>
|
|
363
|
+
"{button.text}"
|
|
364
|
+
</p>
|
|
365
|
+
{/if}
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
{/each}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
{/if}
|
|
372
|
+
</div>
|
|
373
|
+
{/if}
|
|
374
|
+
{/each}
|
|
375
|
+
</div>
|
|
376
|
+
{/if}
|
|
377
|
+
</div>
|
|
378
|
+
{/if}
|
|
379
|
+
{/each}
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<!-- Footer Stats -->
|
|
383
|
+
<div class="max-w-7xl mx-auto px-4 py-8">
|
|
384
|
+
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
|
|
385
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Variant Distribution</h3>
|
|
386
|
+
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
|
387
|
+
{#each allVariants as variant}
|
|
388
|
+
{@const count = Object.values(manifest.categories)
|
|
389
|
+
.flatMap(routes => routes.flatMap(r => r.buttons))
|
|
390
|
+
.filter(b => b.variant === variant).length}
|
|
391
|
+
<div class="flex items-center gap-2">
|
|
392
|
+
<span class="px-2 py-1 text-xs font-medium rounded {getVariantColor(variant)}">{variant}</span>
|
|
393
|
+
<span class="text-sm text-gray-600 dark:text-gray-400">{count}</span>
|
|
394
|
+
</div>
|
|
395
|
+
{/each}
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
<!-- Preview Modal -->
|
|
401
|
+
{#if previewModal.open}
|
|
402
|
+
<div class="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
|
403
|
+
<div class="relative w-full max-w-6xl h-[85vh] bg-white dark:bg-gray-800 rounded-xl shadow-2xl overflow-hidden flex flex-col">
|
|
404
|
+
<!-- Modal Header -->
|
|
405
|
+
<div class="flex items-center justify-between px-4 py-3 bg-gray-100 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600">
|
|
406
|
+
<div class="flex items-center gap-3">
|
|
407
|
+
<span class="text-lg font-semibold text-gray-900 dark:text-white">{previewModal.title}</span>
|
|
408
|
+
<code class="text-sm text-gray-500 dark:text-gray-400">{previewModal.file}</code>
|
|
409
|
+
</div>
|
|
410
|
+
<div class="flex items-center gap-2">
|
|
411
|
+
<a
|
|
412
|
+
href={previewModal.url}
|
|
413
|
+
target="_blank"
|
|
414
|
+
rel="noopener"
|
|
415
|
+
class="px-3 py-1.5 text-sm rounded-lg bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-300"
|
|
416
|
+
>
|
|
417
|
+
Open in new tab ↗
|
|
418
|
+
</a>
|
|
419
|
+
<button
|
|
420
|
+
onclick={closePreview}
|
|
421
|
+
class="p-2 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-500 dark:text-gray-400"
|
|
422
|
+
>
|
|
423
|
+
✕
|
|
424
|
+
</button>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
|
|
428
|
+
<!-- Iframe Content -->
|
|
429
|
+
<div class="flex-1 relative">
|
|
430
|
+
<iframe
|
|
431
|
+
src={previewModal.url}
|
|
432
|
+
class="absolute inset-0 w-full h-full border-0"
|
|
433
|
+
title="Page preview"
|
|
434
|
+
/>
|
|
435
|
+
</div>
|
|
436
|
+
|
|
437
|
+
<!-- Modal Footer -->
|
|
438
|
+
<div class="px-4 py-2 bg-gray-50 dark:bg-gray-700/50 border-t border-gray-200 dark:border-gray-600 text-xs text-gray-500 dark:text-gray-400">
|
|
439
|
+
⚠️ Preview requires micdrop-frontend dev server running at {FRONTEND_DEV_URL}
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
{/if}
|
|
444
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export default ButtonAuditDashboard;
|
|
2
|
+
type ButtonAuditDashboard = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<Record<string, never>>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const ButtonAuditDashboard: import("svelte").Component<Record<string, never>, {}, "">;
|
|
7
|
+
//# sourceMappingURL=ButtonAuditDashboard.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonAuditDashboard.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/stories/ButtonAuditDashboard.svelte.js"],"names":[],"mappings":";;;;;AAwYA,8FAAmE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script module>
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
|
+
import ButtonAuditReview from "./ButtonAuditReview.svelte";
|
|
4
|
+
|
|
5
|
+
const { Story } = defineMeta({
|
|
6
|
+
title: "Design System/Button Audit Review",
|
|
7
|
+
component: ButtonAuditReview,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'fullscreen',
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Story name="All Issues" />
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default ButtonAuditReview;
|
|
2
|
+
type ButtonAuditReview = SvelteComponent<{
|
|
3
|
+
[x: string]: never;
|
|
4
|
+
}, {
|
|
5
|
+
[evt: string]: CustomEvent<any>;
|
|
6
|
+
}, {}> & {
|
|
7
|
+
$$bindings?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
declare const ButtonAuditReview: $$__sveltets_2_IsomorphicComponent<{
|
|
10
|
+
[x: string]: never;
|
|
11
|
+
}, {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
}, {}, {}, string>;
|
|
14
|
+
import ButtonAuditReview from "./ButtonAuditReview.svelte";
|
|
15
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
16
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
17
|
+
$$bindings?: Bindings;
|
|
18
|
+
} & Exports;
|
|
19
|
+
(internal: unknown, props: {
|
|
20
|
+
$$events?: Events;
|
|
21
|
+
$$slots?: Slots;
|
|
22
|
+
}): Exports & {
|
|
23
|
+
$set?: any;
|
|
24
|
+
$on?: any;
|
|
25
|
+
};
|
|
26
|
+
z_$$bindings?: Bindings;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=ButtonAuditReview.stories.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonAuditReview.stories.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/stories/ButtonAuditReview.stories.svelte.js"],"names":[],"mappings":";;;;;;;;AA4BA;;;;mBAA6H;8BAxB7F,4BAA4B;6CAef,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,OAAO,OAAO,QAAQ;IAC3L,cAAc,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,WAAW,OAAO,SAAS;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,eAAe,QAAQ,CAAC"}
|