Responsive layout

This commit is contained in:
Codex
2026-03-07 07:05:48 +00:00
parent 5638e78b84
commit 8afb903d7b
4 changed files with 293 additions and 98 deletions

View File

@@ -1,13 +1,54 @@
<script lang="ts">
import { page } from '$app/stores';
import { categories, getCalculatorsByCategory } from '$lib/data/calculators';
import { categories, getCalculatorsByCategory, type CalculatorDef } from '$lib/data/calculators';
let expandedCategory = '';
let expandedUnits: Record<string, string> = {};
$: currentPath = $page.url.pathname;
type UnitGroup = {
label: string;
conversions: CalculatorDef[];
};
$: categoryUnitGroups = Object.entries(categories).map(([key, meta]) => {
const buckets = new Map<string, CalculatorDef[]>();
const calcs = getCalculatorsByCategory(key);
calcs.forEach(calc => {
const unit = calc.labels.in1;
const existing = buckets.get(unit);
if (existing) {
existing.push(calc);
} else {
buckets.set(unit, [calc]);
}
});
const units = [...buckets.entries()]
.sort(([a], [b]) => a.localeCompare(b))
.map(([label, conversions]) => ({
label,
conversions: conversions.slice().sort((a, b) => a.labels.in2.localeCompare(b.labels.in2)),
}));
return { key, meta, units };
});
function toggle(cat: string) {
expandedCategory = expandedCategory === cat ? '' : cat;
const wasOpen = expandedCategory === cat;
expandedCategory = wasOpen ? '' : cat;
if (wasOpen) {
expandedUnits = { ...expandedUnits, [cat]: '' };
}
}
function toggleUnit(category: string, unitLabel: string) {
expandedUnits = {
...expandedUnits,
[category]: expandedUnits[category] === unitLabel ? '' : unitLabel,
};
}
export let open = false;
@@ -19,32 +60,46 @@
<button class="close-btn" on:click={() => (open = false)} aria-label="Close sidebar"></button>
</div>
<nav>
{#each Object.entries(categories) as [key, meta]}
{@const calcs = getCalculatorsByCategory(key)}
{#each categoryUnitGroups as group}
<div class="cat-section">
<button
class="cat-toggle"
class:active={expandedCategory === key || currentPath.includes(`/category/${key}`)}
on:click={() => toggle(key)}
class:active={expandedCategory === group.key || currentPath.includes(`/category/${group.key}`)}
on:click={() => toggle(group.key)}
>
<span class="cat-icon">{meta.icon}</span>
<span class="cat-label">{meta.label}</span>
<span class="chevron" class:expanded={expandedCategory === key}></span>
<span class="cat-icon">{group.meta.icon}</span>
<span class="cat-label">{group.meta.label}</span>
<span class="chevron" class:expanded={expandedCategory === group.key}></span>
</button>
{#if expandedCategory === key}
<ul class="cat-list" >
{#each calcs as calc}
<li>
<a
href="/{calc.slug}"
class:current={currentPath === `/${calc.slug}`}
{#if expandedCategory === group.key}
<ul class="cat-list">
{#each group.units as unit (unit.label)}
<li class="unit-item">
<button
type="button"
class="unit-toggle"
class:expanded={expandedUnits[group.key] === unit.label}
aria-expanded={expandedUnits[group.key] === unit.label}
on:click={() => toggleUnit(group.key, unit.label)}
>
{calc.name}
</a>
<span class="unit-label">{unit.label}</span>
<span class="chevron" class:expanded={expandedUnits[group.key] === unit.label}></span>
</button>
{#if expandedUnits[group.key] === unit.label}
<ul class="unit-list">
{#each unit.conversions as calc}
<li>
<a href="/{calc.slug}" class:current={currentPath === `/${calc.slug}`}>
{calc.name}
</a>
</li>
{/each}
</ul>
{/if}
</li>
{/each}
<li>
<a href="/category/{key}" class="view-all">View all {meta.label}</a>
<a href="/category/{group.key}" class="view-all">View all {group.meta.label}</a>
</li>
</ul>
{/if}
@@ -166,6 +221,53 @@
color: var(--accent) !important;
}
.unit-item {
margin: 0;
}
.unit-toggle {
display: flex;
align-items: center;
width: 100%;
padding: 0.4rem 1rem 0.4rem 2.5rem;
border: none;
background: none;
cursor: pointer;
font-size: 0.8rem;
color: var(--text-muted);
gap: 0.4rem;
text-align: left;
transition: color 0.15s, background 0.15s;
}
.unit-toggle:hover {
background: var(--hover-bg);
}
.unit-toggle.expanded {
color: var(--accent);
font-weight: 600;
}
.unit-label {
flex: 1;
}
.unit-list {
list-style: none;
margin: 0;
padding: 0;
}
.unit-list li a {
display: block;
padding: 0.25rem 1rem 0.25rem 3.4rem;
font-size: 0.78rem;
color: var(--text-muted);
text-decoration: none;
transition: color 0.15s, background 0.15s;
border-radius: 0;
}
.unit-list li a:hover {
color: var(--accent);
background: var(--hover-bg);
}
.overlay {
display: none;
}