Responsive layout
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user