feat(web): add AES-CBC page with file encryption and UI improvements

This commit is contained in:
Kristofers Solo 2025-12-31 05:45:42 +02:00
parent 651651780f
commit 721c712ba3
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
4 changed files with 73 additions and 53 deletions

View File

@ -1,4 +1,3 @@
use crate::components::radio_button::RadioButton;
use cipher_factory::prelude::*;
use leptos::{prelude::*, tachys::dom::event_target_value};
use std::str::FromStr;
@ -11,7 +10,7 @@ pub fn ConfigurationSection(
set_mode: WriteSignal<OperationMode>,
output_fmt: ReadSignal<OutputFormat>,
update_output: impl Fn(OutputFormat) + Copy + Send + 'static,
) -> AnyView {
) -> impl IntoView {
let handle_format_change = move |ev| {
let val = event_target_value(&ev);
let fmt = OutputFormat::from_str(&val).unwrap_or_default();
@ -39,39 +38,49 @@ pub fn ConfigurationSection(
view! {
<div class="form-group">
<label>"Configuration"</label>
<label>"Mode"</label>
<div class="controls-row">
<div class="radio-group">
<RadioButton value=OperationMode::Encrypt current=mode set_current=set_mode />
<RadioButton value=OperationMode::Decrypt current=mode set_current=set_mode />
<div class="mode-toggle">
<button
type="button"
class=move || if mode.get() == OperationMode::Encrypt { "mode-btn active" } else { "mode-btn" }
on:click=move |_| set_mode(OperationMode::Encrypt)
>
"Encrypt"
</button>
<button
type="button"
class=move || if mode.get() == OperationMode::Decrypt { "mode-btn active" } else { "mode-btn" }
on:click=move |_| set_mode(OperationMode::Decrypt)
>
"Decrypt"
</button>
</div>
{move || {
if mode.get() != OperationMode::Decrypt {
return view! { <span></span> }.into_any();
}
view! {
<div class="format-controls-box">
<div class="format-controls">
<label>"Output format:"</label>
<select
on:wheel=handle_format_wheel
on:change=handle_format_change
prop:value=move || output_fmt.get().to_string()
>
{OutputFormat::iter()
.map(|fmt| {
view! {
<option value=fmt.to_string()>{fmt.to_string()}</option>
}
})
.collect_view()}
</select>
</div>
<div class="format-select">
<label>"Output:"</label>
<select
on:wheel=handle_format_wheel
on:change=handle_format_change
prop:value=move || output_fmt.get().to_string()
>
{OutputFormat::iter()
.map(|fmt| {
view! {
<option value=fmt.to_string()>{fmt.to_string()}</option>
}
})
.collect_view()}
</select>
</div>
}
.into_any()
}}
</div>
</div>
}.into_any()
}
}

View File

@ -6,5 +6,4 @@ pub mod file_input;
pub mod iv_input;
pub mod key_input;
pub mod output_box;
pub mod radio_button;
pub mod text_input;

View File

@ -1,25 +0,0 @@
use cipher_factory::prelude::OperationMode;
use leptos::prelude::*;
#[component]
pub fn RadioButton(
value: OperationMode,
current: ReadSignal<OperationMode>,
set_current: WriteSignal<OperationMode>,
) -> AnyView {
view! {
<div class="radio-button">
<label>
<input
type="radio"
name="crypto-mode"
value=value.to_string()
prop:checked=move || current.get() == value
on:change=move |_| set_current.set(value)
/>
{value.to_string()}
</label>
</div>
}
.into_any()
}

View File

@ -701,11 +701,13 @@ main {
}
}
// Input mode toggle (Text/File switcher)
// Mode toggle buttons (shared style for encrypt/decrypt and text/file)
.mode-toggle,
.input-mode-toggle {
display: flex;
gap: 4px;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 6px;
padding: 2px;
@ -713,10 +715,10 @@ main {
background: transparent;
border: none;
color: var(--text-muted);
padding: 4px 12px;
padding: 6px 14px;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
font-size: 0.85rem;
font-weight: 600;
transition: all 0.2s;
@ -731,6 +733,41 @@ main {
}
}
// Format selector (appears next to mode toggle in decrypt mode)
.format-select {
display: flex;
align-items: center;
gap: 8px;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 6px;
padding: 2px 10px;
animation: fadeIn 0.2s ease-in-out;
label {
margin: 0;
color: var(--text-muted);
font-weight: 500;
font-size: 0.85rem;
text-transform: none;
letter-spacing: normal;
}
select {
background: var(--bg-body);
border: none;
border-radius: 4px;
color: var(--text-main);
padding: 6px 8px;
font-size: 0.85rem;
cursor: pointer;
&:focus {
outline: none;
}
}
}
// Textarea wrapper
.textarea-wrapper {
position: relative;