2026-01-12 13:46:58 -06:00
|
|
|
---
|
|
|
|
|
import { BlitzKitIcon } from "../icons/BlitzKit";
|
2026-01-13 20:33:14 -06:00
|
|
|
import { LinkI18n } from "./LinkI18n";
|
2026-01-18 14:51:55 -06:00
|
|
|
import { Text } from "./Text";
|
|
|
|
|
import { api } from "../core/api/dynamic";
|
2026-01-18 18:38:26 -06:00
|
|
|
import { tools } from "../core/tools/tools";
|
|
|
|
|
import type { Tool } from "../core/tools/tools";
|
2026-01-13 20:33:14 -06:00
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
locale: string;
|
2026-01-18 18:38:26 -06:00
|
|
|
selectedTool?: Tool;
|
2026-01-13 20:33:14 -06:00
|
|
|
}
|
|
|
|
|
|
2026-01-18 18:38:26 -06:00
|
|
|
const { locale, selectedTool } = Astro.props;
|
2026-01-18 14:51:55 -06:00
|
|
|
const strings = await api.strings(locale);
|
2026-01-18 18:38:26 -06:00
|
|
|
const toolsArray = Object.values(tools);
|
|
|
|
|
|
|
|
|
|
const toolsSorted =
|
|
|
|
|
selectedTool === undefined
|
|
|
|
|
? toolsArray
|
|
|
|
|
: [selectedTool, ...toolsArray.filter((tool) => tool !== selectedTool)];
|
2026-01-12 13:46:58 -06:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
<style>
|
2026-01-15 13:48:37 -06:00
|
|
|
#navbar {
|
2026-01-18 15:14:26 -06:00
|
|
|
position: fixed;
|
|
|
|
|
bottom: 0;
|
2026-01-13 20:33:14 -06:00
|
|
|
left: 0;
|
2026-01-18 15:14:26 -06:00
|
|
|
width: 100%;
|
2026-01-12 13:46:58 -06:00
|
|
|
|
|
|
|
|
display: flex;
|
2026-01-18 15:14:26 -06:00
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
2026-01-12 13:46:58 -06:00
|
|
|
|
|
|
|
|
z-index: 1;
|
2026-01-18 15:14:26 -06:00
|
|
|
background-color: var(--gray-a5);
|
2026-01-15 13:48:37 -06:00
|
|
|
box-shadow: var(--shadow-none);
|
2026-01-18 15:14:26 -06:00
|
|
|
backdrop-filter: var(--blur-glass) brightness(5%);
|
|
|
|
|
border-radius: var(--space-4) var(--space-4) 0 0;
|
2026-01-15 13:48:37 -06:00
|
|
|
|
|
|
|
|
transition-duration: var(--duration-brief);
|
|
|
|
|
|
|
|
|
|
&[data-sticky="true"] {
|
2026-01-18 15:14:26 -06:00
|
|
|
transform: translateY(100%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bar {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: var(--space-6);
|
|
|
|
|
height: var(--space-2);
|
|
|
|
|
margin-top: var(--space-3);
|
|
|
|
|
|
|
|
|
|
background-color: var(--white-a2);
|
|
|
|
|
|
|
|
|
|
border-radius: var(--max-size);
|
2026-01-15 13:48:37 -06:00
|
|
|
}
|
2026-01-13 20:33:14 -06:00
|
|
|
|
|
|
|
|
.content {
|
2026-01-18 15:14:26 -06:00
|
|
|
width: 100%;
|
2026-01-13 20:33:14 -06:00
|
|
|
max-width: calc(var(--page-width) + 2 * var(--space-4));
|
2026-01-12 13:46:58 -06:00
|
|
|
|
2026-01-18 18:38:26 -06:00
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(4, 1fr);
|
2026-01-12 13:46:58 -06:00
|
|
|
|
2026-01-18 14:51:55 -06:00
|
|
|
.action {
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
2026-01-12 13:46:58 -06:00
|
|
|
display: flex;
|
2026-01-18 14:51:55 -06:00
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: var(--space-3);
|
|
|
|
|
gap: var(--space-2);
|
2026-01-12 13:46:58 -06:00
|
|
|
|
2026-01-18 14:51:55 -06:00
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--color-regular);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
color: var(--gray-11);
|
2026-01-13 20:33:14 -06:00
|
|
|
|
2026-01-18 14:51:55 -06:00
|
|
|
&[data-selected="true"] {
|
|
|
|
|
color: var(--accent-12);
|
2026-01-13 20:33:14 -06:00
|
|
|
|
2026-01-18 14:51:55 -06:00
|
|
|
.selector {
|
|
|
|
|
background-color: var(--accent-5);
|
2026-01-13 20:33:14 -06:00
|
|
|
}
|
|
|
|
|
|
2026-01-18 14:51:55 -06:00
|
|
|
.label {
|
|
|
|
|
color: var(--gray-12) !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.selector {
|
|
|
|
|
padding: var(--space-2);
|
|
|
|
|
border-radius: var(--max-size);
|
|
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: var(--space-6);
|
|
|
|
|
|
2026-01-13 20:33:14 -06:00
|
|
|
.icon {
|
|
|
|
|
height: 1.25em;
|
2026-01-18 18:38:26 -06:00
|
|
|
width: 100%;
|
2026-01-13 20:33:14 -06:00
|
|
|
vertical-align: top;
|
|
|
|
|
}
|
2026-01-12 13:46:58 -06:00
|
|
|
}
|
2026-01-18 14:51:55 -06:00
|
|
|
|
|
|
|
|
.label {
|
|
|
|
|
font-size: var(--font-size-minor);
|
|
|
|
|
}
|
2026-01-12 13:46:58 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
2026-01-15 13:48:37 -06:00
|
|
|
<script>
|
|
|
|
|
const navbar = document.getElementById("navbar")!;
|
2026-01-18 15:14:26 -06:00
|
|
|
let lastScrollY = window.scrollY;
|
2026-01-15 13:48:37 -06:00
|
|
|
|
|
|
|
|
function handleScroll() {
|
2026-01-18 15:14:26 -06:00
|
|
|
const dy = window.scrollY - lastScrollY;
|
|
|
|
|
lastScrollY = window.scrollY;
|
|
|
|
|
navbar.dataset.sticky = `${dy > 0}`;
|
2026-01-15 13:48:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleScroll();
|
|
|
|
|
window.addEventListener("scroll", handleScroll);
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<div id="navbar">
|
2026-01-18 15:14:26 -06:00
|
|
|
<div class="bar"></div>
|
|
|
|
|
|
2026-01-13 20:33:14 -06:00
|
|
|
<div class="content">
|
2026-01-18 18:38:26 -06:00
|
|
|
<LinkI18n
|
|
|
|
|
{locale}
|
|
|
|
|
href="/"
|
|
|
|
|
className="action"
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
data-selected={selectedTool === undefined}
|
|
|
|
|
>
|
2026-01-18 14:51:55 -06:00
|
|
|
<div class="selector">
|
2026-01-13 20:33:14 -06:00
|
|
|
<BlitzKitIcon className="icon" />
|
|
|
|
|
</div>
|
2026-01-18 14:51:55 -06:00
|
|
|
<Text lowContrast className="label">{strings.navbar.home}</Text>
|
|
|
|
|
</LinkI18n>
|
2026-01-13 20:33:14 -06:00
|
|
|
|
2026-01-18 18:38:26 -06:00
|
|
|
{
|
|
|
|
|
toolsSorted.map(({ id, Icon }) => (
|
|
|
|
|
<LinkI18n
|
|
|
|
|
key={id}
|
|
|
|
|
{locale}
|
|
|
|
|
href={`/${id}`}
|
|
|
|
|
className="action"
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
data-selected={selectedTool?.id === id}
|
|
|
|
|
>
|
|
|
|
|
<div class="selector">
|
|
|
|
|
<Icon className="icon" />
|
|
|
|
|
</div>
|
2026-01-18 17:55:38 -06:00
|
|
|
|
2026-01-18 18:38:26 -06:00
|
|
|
<Text lowContrast className="label">
|
|
|
|
|
{strings.tools[id as keyof typeof strings.tools].name}
|
|
|
|
|
</Text>
|
|
|
|
|
</LinkI18n>
|
|
|
|
|
))
|
|
|
|
|
}
|
2026-01-12 13:46:58 -06:00
|
|
|
</div>
|
|
|
|
|
</div>
|