Add version history, screenshots, embed generator, collections, npm search, format-on-save, and custom fonts
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { listFiddles, listTags } from './api.js';
|
||||
import { listFiddles, listTags, listCollections, getCollection } from './api.js';
|
||||
|
||||
const $ = (sel) => document.querySelector(sel);
|
||||
let debounceTimer = null;
|
||||
let currentPage = 1;
|
||||
let activeTag = '';
|
||||
let currentView = 'fiddles';
|
||||
|
||||
const JS_TYPE_LABELS = {
|
||||
javascript: 'JS',
|
||||
@@ -27,8 +28,8 @@ function relativeTime(dateStr) {
|
||||
return new Date(dateStr).toLocaleDateString();
|
||||
}
|
||||
|
||||
function renderCards(fiddles) {
|
||||
const grid = $('#fiddle-grid');
|
||||
function renderCards(fiddles, gridSelector = '#fiddle-grid') {
|
||||
const grid = $(gridSelector);
|
||||
if (!fiddles.length) {
|
||||
grid.innerHTML = '<div class="empty-state">No fiddles found. <a href="/new" style="color: var(--accent)">Create one!</a></div>';
|
||||
return;
|
||||
@@ -37,15 +38,20 @@ function renderCards(fiddles) {
|
||||
const preview = f.js_preview || f.html_preview || '';
|
||||
const badge = JS_TYPE_LABELS[f.js_type] || 'JS';
|
||||
const tags = (f.tags || []).map(t => `<span class="card-tag">${esc(t.name)}</span>`).join('');
|
||||
const thumb = f.screenshot
|
||||
? `<img class="card-thumbnail" src="${f.screenshot}" alt="" loading="lazy">`
|
||||
: (preview ? `<div class="card-preview" style="padding:12px 16px">${esc(preview)}</div>` : '');
|
||||
return `
|
||||
<a href="/f/${f.id}" class="fiddle-card">
|
||||
<div class="card-title">${esc(f.title)}</div>
|
||||
<div class="card-meta">
|
||||
<span class="card-badge">${badge}</span>
|
||||
<span>${relativeTime(f.updated_at)}</span>
|
||||
${thumb}
|
||||
<div class="card-body">
|
||||
<div class="card-title">${esc(f.title)}</div>
|
||||
<div class="card-meta">
|
||||
<span class="card-badge">${badge}</span>
|
||||
<span>${relativeTime(f.updated_at)}</span>
|
||||
</div>
|
||||
${tags ? `<div class="card-tags">${tags}</div>` : ''}
|
||||
</div>
|
||||
${preview ? `<div class="card-preview">${esc(preview)}</div>` : ''}
|
||||
${tags ? `<div class="card-tags">${tags}</div>` : ''}
|
||||
</a>
|
||||
`;
|
||||
}).join('');
|
||||
@@ -130,6 +136,64 @@ $('#filter-sort').addEventListener('change', () => {
|
||||
fetchAndRender();
|
||||
});
|
||||
|
||||
// Browse tabs
|
||||
document.querySelectorAll('.browse-tab').forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
currentView = tab.dataset.view;
|
||||
document.querySelectorAll('.browse-tab').forEach(t => t.classList.toggle('active', t === tab));
|
||||
$('#fiddles-view').style.display = currentView === 'fiddles' ? '' : 'none';
|
||||
$('#collections-view').style.display = currentView === 'collections' ? '' : 'none';
|
||||
if (currentView === 'collections') renderCollections();
|
||||
});
|
||||
});
|
||||
|
||||
async function renderCollections() {
|
||||
const grid = $('#collections-grid');
|
||||
const detail = $('#collection-detail');
|
||||
grid.style.display = '';
|
||||
detail.style.display = 'none';
|
||||
|
||||
try {
|
||||
const { collections } = await listCollections();
|
||||
if (!collections.length) {
|
||||
grid.innerHTML = '<div class="empty-state">No collections yet</div>';
|
||||
return;
|
||||
}
|
||||
grid.innerHTML = collections.map(c => `
|
||||
<div class="collection-card" data-id="${c.id}">
|
||||
<div class="collection-card-name">${esc(c.name)}</div>
|
||||
<div class="collection-card-desc">${esc(c.description || '')}</div>
|
||||
<div class="collection-card-count">${c.fiddle_count} fiddle${c.fiddle_count !== 1 ? 's' : ''}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
grid.querySelectorAll('.collection-card').forEach(card => {
|
||||
card.addEventListener('click', () => showCollectionDetail(card.dataset.id));
|
||||
});
|
||||
} catch (e) {
|
||||
grid.innerHTML = `<div class="empty-state">Error: ${esc(e.message)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function showCollectionDetail(id) {
|
||||
const grid = $('#collections-grid');
|
||||
const detail = $('#collection-detail');
|
||||
grid.style.display = 'none';
|
||||
detail.style.display = '';
|
||||
|
||||
try {
|
||||
const col = await getCollection(id);
|
||||
$('#collection-header').textContent = col.name;
|
||||
$('#collection-desc').textContent = col.description || '';
|
||||
renderCards(col.fiddles || [], '#collection-fiddles');
|
||||
} catch (e) {
|
||||
$('#collection-header').textContent = 'Error';
|
||||
$('#collection-desc').textContent = e.message;
|
||||
}
|
||||
}
|
||||
|
||||
$('#collection-back').addEventListener('click', () => renderCollections());
|
||||
|
||||
// Init
|
||||
renderTagsBar();
|
||||
fetchAndRender();
|
||||
|
||||
Reference in New Issue
Block a user