feat(web): add footer

This commit is contained in:
2025-11-26 17:33:10 +02:00
parent a93ff3f920
commit 898d5f7195
5 changed files with 144 additions and 74 deletions

View File

@@ -1,11 +1,10 @@
use crate::pages::{aes::AesPage, des::DesPage, home::Home};
use crate::pages::{aes::AesPage, des::DesPage, footer::Footer, header::Header, home::Home};
use leptos::prelude::*;
use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_router::{
StaticSegment,
components::{A, Route, Router, Routes},
components::{Route, Router, Routes},
};
use std::fmt::Display;
#[must_use]
pub fn shell(options: LeptosOptions) -> impl IntoView {
@@ -26,60 +25,15 @@ pub fn shell(options: LeptosOptions) -> impl IntoView {
}
}
#[derive(Clone, Copy, PartialEq)]
enum Theme {
Light,
Dark,
}
impl Theme {
const fn inverse(self) -> Self {
match self {
Self::Light => Self::Dark,
Self::Dark => Self::Light,
}
}
}
impl Display for Theme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Light => "☀️ Light",
Self::Dark => "🌙 Dark",
};
f.write_str(s)
}
}
#[component]
// Provides context that manages stylesheets, titles, meta tags, etc.
pub fn App() -> impl IntoView {
provide_meta_context();
let (theme, set_theme) = signal(Theme::Dark);
let toggle_theme = move |_| {
set_theme.update(|t| *t = t.inverse());
if let Some(body) = document().body() {
let class_list = body.class_list();
match theme.get() {
Theme::Light => {
let _ = class_list.remove_1("dark-theme");
let _ = class_list.add_1("light-theme");
}
Theme::Dark => {
let _ = class_list.remove_1("light-theme");
let _ = class_list.add_1("dark-theme");
}
}
}
};
view! {
// injects a stylesheet into the document <head>
// id=leptos means cargo-leptos will hot-reload this stylesheet
<Stylesheet id="leptos" href="/pkg/web2.css" />
<Stylesheet id="leptos" href="/pkg/web.css" />
// sets the document title
<Title text="Cipher Workshop" />
@@ -87,22 +41,7 @@ pub fn App() -> impl IntoView {
// content for this welcome page
<Router>
<div class="app-containter">
<nav class="main-nav">
<ul>
<li>
<A href="/">"Home"</A>
</li>
<li>
<A href="/des">"DES"</A>
</li>
<li>
<A href="/aes">"AES"</A>
</li>
</ul>
<button class="theme-toggle" on:click=toggle_theme>
{move || theme.get().to_string()}
</button>
</nav>
<Header />
<main>
<Routes fallback=|| "Page not found.".into_view()>
<Route path=StaticSegment("/") view=Home />
@@ -110,6 +49,7 @@ pub fn App() -> impl IntoView {
<Route path=StaticSegment("/aes") view=AesPage />
</Routes>
</main>
<Footer />
</div>
</Router>
}

23
web/src/pages/footer.rs Normal file
View File

@@ -0,0 +1,23 @@
use leptos::prelude::*;
use leptos_router::components::A;
#[component]
pub fn Footer() -> impl IntoView {
view! {
<footer class="app-footer">
<div class="footer-content">
<p>
"🔒 " <strong>"Client-Side Security:"</strong>
" All encryption and decryption operations happen entirely in your browser. "
"No data is ever sent to a server. "
"You can verify this by disconnecting your internet."
</p>
<div class="footer-links">
<A href="https://github.com/kristoferssolo/cipher-workshop" target="_blank">
"View Source on GitHub"
</A>
</div>
</div>
</footer>
}
}

70
web/src/pages/header.rs Normal file
View File

@@ -0,0 +1,70 @@
use leptos::prelude::*;
use leptos_router::components::A;
use std::fmt::Display;
#[component]
pub fn Header() -> impl IntoView {
let (theme, set_theme) = signal(Theme::Dark);
let toggle_theme = move |_| {
set_theme.update(|t| *t = t.inverse());
if let Some(body) = document().body() {
let class_list = body.class_list();
match theme.get() {
Theme::Light => {
let _ = class_list.remove_1("dark-theme");
let _ = class_list.add_1("light-theme");
}
Theme::Dark => {
let _ = class_list.remove_1("light-theme");
let _ = class_list.add_1("dark-theme");
}
}
}
};
view! {
<nav class="main-nav">
<ul>
<li>
<A href="/">"Home"</A>
</li>
<li>
<A href="/des">"DES"</A>
</li>
<li>
<A href="/aes">"AES"</A>
</li>
</ul>
<button class="theme-toggle" on:click=toggle_theme>
{move || theme.get().to_string()}
</button>
</nav>
}
}
#[derive(Clone, Copy, PartialEq)]
enum Theme {
Light,
Dark,
}
impl Theme {
const fn inverse(self) -> Self {
match self {
Self::Light => Self::Dark,
Self::Dark => Self::Light,
}
}
}
impl Display for Theme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Light => "☀️ Light",
Self::Dark => "🌙 Dark",
};
f.write_str(s)
}
}

View File

@@ -1,3 +1,5 @@
pub mod aes;
pub mod des;
pub mod footer;
pub mod header;
pub mod home;