Skip to content

Commit 8057863

Browse files
committed
added list view
1 parent 9ad774a commit 8057863

1 file changed

Lines changed: 102 additions & 26 deletions

File tree

packages/index.html

Lines changed: 102 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,26 @@ <h4 class="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-400">Bun
122122
</aside>
123123

124124
<!-- Results -->
125-
<section class="rounded-2xl border border-gray-200 bg-white/40 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-white/[0.03] sm:p-5">
125+
<section class="min-w-0 rounded-2xl border border-gray-200 bg-white/40 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-white/[0.03] sm:p-5">
126126
<div class="mb-4 flex flex-wrap items-center justify-between gap-3 border-b border-gray-200 pb-4 dark:border-white/10">
127127
<div id="count" class="text-sm text-gray-600 dark:text-gray-300"></div>
128-
<select id="sort" class="rounded-xl border border-gray-200 bg-white/70 px-3 py-2 text-sm text-gray-800 outline-none focus:border-brand dark:border-white/10 dark:bg-white/10 dark:text-gray-100">
129-
<option value="popular">Sort: Popular</option>
130-
<option value="stars">Most stars</option>
131-
<option value="forks">Most forks</option>
132-
<option value="updated">Recently updated</option>
133-
<option value="name">Name (A–Z)</option>
134-
</select>
128+
<div class="flex items-center gap-3">
129+
<div id="viewToggle" class="inline-flex rounded-xl border border-gray-200 bg-white/70 p-1 shadow-sm dark:border-white/10 dark:bg-white/5" role="group" aria-label="View layout">
130+
<button id="viewGrid" type="button" title="Grid view" aria-label="Grid view" class="rounded-lg px-2.5 py-1.5 text-gray-500 dark:text-gray-400">
131+
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>
132+
</button>
133+
<button id="viewList" type="button" title="List view" aria-label="List view" class="rounded-lg px-2.5 py-1.5 text-gray-500 dark:text-gray-400">
134+
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><circle cx="3.5" cy="6" r="1.2" fill="currentColor" stroke="none"/><circle cx="3.5" cy="12" r="1.2" fill="currentColor" stroke="none"/><circle cx="3.5" cy="18" r="1.2" fill="currentColor" stroke="none"/></svg>
135+
</button>
136+
</div>
137+
<select id="sort" class="rounded-xl border border-gray-200 bg-white/70 px-3 py-2 text-sm text-gray-800 outline-none focus:border-brand dark:border-white/10 dark:bg-white/10 dark:text-gray-100">
138+
<option value="popular">Sort: Popular</option>
139+
<option value="stars">Most stars</option>
140+
<option value="forks">Most forks</option>
141+
<option value="updated">Recently updated</option>
142+
<option value="name">Name (A–Z)</option>
143+
</select>
144+
</div>
135145
</div>
136146
<div id="grid" class="grid gap-4 grid-cols-[repeat(auto-fill,minmax(18rem,1fr))]"></div>
137147
</section>
@@ -148,7 +158,7 @@ <h4 class="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-400">Bun
148158
<div id="toast" class="pointer-events-none fixed bottom-6 left-1/2 z-50 -translate-x-1/2 rounded-2xl border border-gray-200 bg-white px-5 py-3 text-sm opacity-0 shadow-lg transition dark:border-white/10 dark:bg-basisbg"></div>
149159

150160
<script>
151-
const state = { search:"", source:"all", category:"all", sort:"popular", view:"packages" };
161+
const state = { search:"", source:"all", category:"all", sort:"popular", view:"packages", layout:"grid" };
152162
const REPO = "BasisVR/BasisPackageManager";
153163
let RAW = [], ALL = [], RAWB = [], bundlesLoaded = false;
154164

@@ -257,6 +267,26 @@ <h4 class="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-400">Bun
257267
document.getElementById('tabBundles').className = state.view==='bundles' ? on : off;
258268
}
259269

270+
// Grid vs list layout — the container class and each button's active state follow state.layout.
271+
const gridClass = () => state.layout==='list'
272+
? 'flex flex-col gap-3'
273+
: 'grid gap-4 grid-cols-[repeat(auto-fill,minmax(18rem,1fr))]';
274+
function viewStyle(){
275+
const on = 'rounded-lg px-2.5 py-1.5 bg-gradient-to-r from-brand to-purple-600 text-white shadow';
276+
const off = 'rounded-lg px-2.5 py-1.5 text-gray-500 hover:text-brand dark:text-gray-400 dark:hover:text-brand';
277+
document.getElementById('viewGrid').className = state.layout==='grid' ? on : off;
278+
document.getElementById('viewList').className = state.layout==='list' ? on : off;
279+
document.getElementById('viewGrid').setAttribute('aria-pressed', state.layout==='grid');
280+
document.getElementById('viewList').setAttribute('aria-pressed', state.layout==='list');
281+
}
282+
function setLayout(layout){
283+
if(state.layout===layout) return;
284+
state.layout = layout;
285+
try{ localStorage.setItem('basis:pkgLayout', layout); }catch{}
286+
viewStyle();
287+
renderGrid();
288+
}
289+
260290
async function switchView(view){
261291
if(state.view===view) return;
262292
state.view = view;
@@ -333,12 +363,8 @@ <h4 class="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-400">Bun
333363
document.querySelectorAll('[data-category]').forEach(el=>el.onclick=()=>{state.category=el.dataset.category;buildFilters();load();});
334364
}
335365

336-
function renderGrid(){
337-
if(state.view==='bundles') return renderBundles();
338-
const grid = document.getElementById('grid');
339-
document.getElementById('count').innerHTML = `<b class="text-gray-900 dark:text-white">${ALL.length}</b> package${ALL.length===1?'':'s'}`;
340-
if(!ALL.length){ grid.innerHTML = `<div class="col-span-full py-20 text-center text-gray-500 dark:text-gray-400"><p class="text-lg font-semibold text-gray-900 dark:text-white">No packages found</p><p class="mt-1">Try a different search or filter.</p></div>`; return; }
341-
grid.innerHTML = ALL.map(p=>`
366+
function pkgCard(p){
367+
return `
342368
<div data-id="${esc(p.id)}" class="group flex cursor-pointer flex-col rounded-2xl border border-gray-200 bg-white/70 p-5 shadow-sm backdrop-blur transition hover:-translate-y-0.5 hover:shadow-lg hover:shadow-brand/20 dark:border-white/10 dark:bg-white/5">
343369
<div class="flex items-start gap-3">
344370
<div class="inline-flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded-xl bg-gray-100 text-xl ring-1 ring-gray-200 dark:bg-white/5 dark:ring-white/10">${imgOrIcon(p)}</div>
@@ -357,7 +383,36 @@ <h3 class="flex flex-wrap items-center gap-2 font-semibold">${esc(p.name)}
357383
${p.license?`<span title="License">⚖ ${esc(p.license)}</span>`:''}
358384
<button class="ml-auto rounded-xl bg-gradient-to-r from-brand to-purple-600 px-3 py-1.5 text-xs font-semibold text-white">Install</button>
359385
</div>
360-
</div>`).join('');
386+
</div>`;
387+
}
388+
function pkgRow(p){
389+
return `
390+
<div data-id="${esc(p.id)}" class="group flex cursor-pointer items-center gap-4 rounded-2xl border border-gray-200 bg-white/70 p-4 shadow-sm backdrop-blur transition hover:shadow-lg hover:shadow-brand/20 dark:border-white/10 dark:bg-white/5">
391+
<div class="inline-flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded-xl bg-gray-100 text-xl ring-1 ring-gray-200 dark:bg-white/5 dark:ring-white/10">${imgOrIcon(p)}</div>
392+
<div class="min-w-0 flex-1">
393+
<h3 class="flex flex-wrap items-center gap-2 font-semibold">
394+
<span class="truncate">${esc(p.name)}</span>
395+
<span class="rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide ${badgeCls(p.source)}">${esc(p.source)}</span>
396+
<span class="text-xs font-normal text-gray-500 dark:text-gray-400">by ${esc(p.author)}${hostOf(p)?' · '+hostOf(p):''}${p.basisVersion?' · Basis '+esc(p.basisVersion):''}</span>
397+
</h3>
398+
<p class="mt-0.5 truncate text-sm text-gray-600 dark:text-gray-300">${esc(p.description)}</p>
399+
</div>
400+
<div class="hidden shrink-0 items-center gap-3 text-xs text-gray-500 md:flex dark:text-gray-400">
401+
<span>⭐ ${fmtNum(p.stars)}</span>
402+
<span>⑂ ${fmtNum(p.forks)}</span>
403+
<span>v${esc(p.version)}</span>
404+
</div>
405+
<button class="shrink-0 rounded-xl bg-gradient-to-r from-brand to-purple-600 px-3 py-1.5 text-xs font-semibold text-white">Install</button>
406+
</div>`;
407+
}
408+
409+
function renderGrid(){
410+
if(state.view==='bundles') return renderBundles();
411+
const grid = document.getElementById('grid');
412+
grid.className = gridClass();
413+
document.getElementById('count').innerHTML = `<b class="text-gray-900 dark:text-white">${ALL.length}</b> package${ALL.length===1?'':'s'}`;
414+
if(!ALL.length){ grid.innerHTML = `<div class="col-span-full py-20 text-center text-gray-500 dark:text-gray-400"><p class="text-lg font-semibold text-gray-900 dark:text-white">No packages found</p><p class="mt-1">Try a different search or filter.</p></div>`; return; }
415+
grid.innerHTML = ALL.map(state.layout==='list' ? pkgRow : pkgCard).join('');
361416
grid.querySelectorAll('[data-id]').forEach(c=>c.onclick=()=>openDetail(c.dataset.id));
362417
}
363418

@@ -429,14 +484,10 @@ <h2 class="flex flex-wrap items-center gap-2 text-xl font-bold">${esc(p.name)} <
429484
m.querySelectorAll('.copy').forEach(b=>b.onclick=e=>{e.stopPropagation();copy(b.dataset.copy,b);});
430485
}
431486

432-
function renderBundles(){
433-
const grid = document.getElementById('grid');
434-
document.getElementById('count').innerHTML = `<b class="text-gray-900 dark:text-white">${ALL.length}</b> bundle${ALL.length===1?'':'s'}`;
435-
if(!ALL.length){ grid.innerHTML = `<div class="col-span-full py-20 text-center text-gray-500 dark:text-gray-400"><p class="text-lg font-semibold text-gray-900 dark:text-white">No bundles yet</p><p class="mt-1">Build one from the Basis Package Manager and submit it.</p></div>`; return; }
436-
grid.innerHTML = ALL.map(b=>{
437-
const n = (b.packages||[]).length;
438-
const basis = b.basisBranch ? `${esc(b.basisBranch)}${b.basisCommit?'@'+esc(b.basisCommit):''}` : '';
439-
return `
487+
function bundleCard(b){
488+
const n = (b.packages||[]).length;
489+
const basis = b.basisBranch ? `${esc(b.basisBranch)}${b.basisCommit?'@'+esc(b.basisCommit):''}` : '';
490+
return `
440491
<div data-bundle="${esc(b.id)}" class="group flex cursor-pointer flex-col rounded-2xl border border-gray-200 bg-white/70 p-5 shadow-sm backdrop-blur transition hover:-translate-y-0.5 hover:shadow-lg hover:shadow-brand/20 dark:border-white/10 dark:bg-white/5">
441492
<div class="flex items-start gap-3">
442493
<div class="inline-flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded-xl bg-gray-100 text-xl ring-1 ring-gray-200 dark:bg-white/5 dark:ring-white/10">${imgOrIcon({image:b.image,icon:b.icon||'🧩'})}</div>
@@ -449,7 +500,30 @@ <h3 class="flex flex-wrap items-center gap-2 font-semibold">${esc(b.name)}
449500
<p class="mt-3 line-clamp-2 text-sm text-gray-600 dark:text-gray-300">${esc(b.description||'')}</p>
450501
<div class="mt-3 flex flex-wrap gap-1.5">${(b.tags||[]).slice(0,4).map(t=>`<span class="rounded-full bg-gray-100 px-2 py-0.5 text-[11px] text-gray-600 dark:bg-white/5 dark:text-gray-300">${esc(t)}</span>`).join('')}</div>
451502
</div>`;
452-
}).join('');
503+
}
504+
function bundleRow(b){
505+
const n = (b.packages||[]).length;
506+
const basis = b.basisBranch ? `${esc(b.basisBranch)}${b.basisCommit?'@'+esc(b.basisCommit):''}` : '';
507+
return `
508+
<div data-bundle="${esc(b.id)}" class="group flex cursor-pointer items-center gap-4 rounded-2xl border border-gray-200 bg-white/70 p-4 shadow-sm backdrop-blur transition hover:shadow-lg hover:shadow-brand/20 dark:border-white/10 dark:bg-white/5">
509+
<div class="inline-flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded-xl bg-gray-100 text-xl ring-1 ring-gray-200 dark:bg-white/5 dark:ring-white/10">${imgOrIcon({image:b.image,icon:b.icon||'🧩'})}</div>
510+
<div class="min-w-0 flex-1">
511+
<h3 class="flex flex-wrap items-center gap-2 font-semibold">
512+
<span class="truncate">${esc(b.name)}</span>
513+
<span class="rounded-full bg-brand/15 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-brand ring-1 ring-brand/30">${n} pkg${n===1?'':'s'}</span>
514+
<span class="text-xs font-normal text-gray-500 dark:text-gray-400">by ${esc(b.author||'—')}${basis?' · Basis '+basis:''}</span>
515+
</h3>
516+
<p class="mt-0.5 truncate text-sm text-gray-600 dark:text-gray-300">${esc(b.description||'')}</p>
517+
</div>
518+
</div>`;
519+
}
520+
521+
function renderBundles(){
522+
const grid = document.getElementById('grid');
523+
grid.className = gridClass();
524+
document.getElementById('count').innerHTML = `<b class="text-gray-900 dark:text-white">${ALL.length}</b> bundle${ALL.length===1?'':'s'}`;
525+
if(!ALL.length){ grid.innerHTML = `<div class="col-span-full py-20 text-center text-gray-500 dark:text-gray-400"><p class="text-lg font-semibold text-gray-900 dark:text-white">No bundles yet</p><p class="mt-1">Build one from the Basis Package Manager and submit it.</p></div>`; return; }
526+
grid.innerHTML = ALL.map(state.layout==='list' ? bundleRow : bundleCard).join('');
453527
grid.querySelectorAll('[data-bundle]').forEach(c=>c.onclick=()=>openBundle(c.dataset.bundle));
454528
}
455529

@@ -649,12 +723,14 @@ <h2 class="text-xl font-bold">${esc(b.name)}</h2>
649723
document.getElementById('submitBtn').onclick = openSubmit;
650724
document.getElementById('tabPackages').onclick = ()=>switchView('packages');
651725
document.getElementById('tabBundles').onclick = ()=>switchView('bundles');
726+
document.getElementById('viewGrid').onclick = ()=>setLayout('grid');
727+
document.getElementById('viewList').onclick = ()=>setLayout('list');
652728

653729
let searchTimer;
654730
document.getElementById('search').oninput = e=>{ clearTimeout(searchTimer); state.search=e.target.value; searchTimer=setTimeout(load,160); };
655731
document.getElementById('sort').onchange = e=>{ state.sort=e.target.value; load(); };
656732

657-
(async function init(){ tabStyle(); await fetchAll(); buildFilters(); load(); if(location.hash==='#bundles') switchView('bundles'); })();
733+
(async function init(){ try{ if(localStorage.getItem('basis:pkgLayout')==='list') state.layout='list'; }catch{} tabStyle(); viewStyle(); await fetchAll(); buildFilters(); load(); if(location.hash==='#bundles') switchView('bundles'); })();
658734
window.addEventListener('hashchange', ()=>switchView(location.hash==='#bundles'?'bundles':'packages'));
659735
</script>
660736

0 commit comments

Comments
 (0)