mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-20 11:04:38 +00:00
refactor(web): make smaller components
This commit is contained in:
parent
486f8957eb
commit
bb7ef246f8
@ -4,15 +4,15 @@ use thiserror::Error;
|
||||
#[derive(Debug, Error, Clone, PartialEq, Eq)]
|
||||
pub enum CipherError {
|
||||
/// Invalid key size for the cipher
|
||||
#[error("Invalid key size: expected {expected} bytes, got {actual}")]
|
||||
#[error("Invalid key size: expected {expected} bytes, got {actual}.")]
|
||||
InvalidKeySize { expected: usize, actual: usize },
|
||||
|
||||
/// Input data doesn't match the cipher's block size
|
||||
#[error("Invalid block size: expected {expected} bytes, got {actual}")]
|
||||
#[error("Invalid block size: expected {expected} bytes, got {actual}.")]
|
||||
InvalidBlockSize { expected: usize, actual: usize },
|
||||
|
||||
/// Error parsing block from string
|
||||
#[error("Error parsing block from string: {0}")]
|
||||
#[error("{0}")]
|
||||
BlockParseError(#[from] BlockError),
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,65 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
||||
}
|
||||
};
|
||||
|
||||
view! {
|
||||
<div class="cipher-card">
|
||||
<div class="card-header">
|
||||
<h2>{algorithm.to_string()}</h2>
|
||||
</div>
|
||||
<ConfigurationSection
|
||||
mode=mode
|
||||
set_mode=set_mode
|
||||
output_fmt=output_fmt
|
||||
update_output=update_output
|
||||
/>
|
||||
<KeyInput set_key_input=set_key_input />
|
||||
<TextInput mode=mode set_text_input=set_text_input />
|
||||
|
||||
<button class="btn-primary" on:click=move |_| handle_submit()>
|
||||
{move || format!("{} using {algorithm}", mode.get())}
|
||||
</button>
|
||||
|
||||
<OutputBox
|
||||
output=output
|
||||
output_fmt=output_fmt
|
||||
copy_to_clipboard=copy_to_clipboard
|
||||
copy_feedback=copy_feedback
|
||||
/>
|
||||
<ErrorBox error_msg=error_msg />
|
||||
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn RadioButton(
|
||||
value: OperationMode,
|
||||
current: ReadSignal<OperationMode>,
|
||||
set_current: WriteSignal<OperationMode>,
|
||||
) -> impl IntoView {
|
||||
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>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn ConfigurationSection(
|
||||
mode: ReadSignal<OperationMode>,
|
||||
set_mode: WriteSignal<OperationMode>,
|
||||
output_fmt: ReadSignal<OutputFormat>,
|
||||
update_output: impl Fn(OutputFormat) + Copy + Send + 'static,
|
||||
) -> impl IntoView {
|
||||
let handle_format_change = move |ev| {
|
||||
let val = event_target_value(&ev);
|
||||
let fmt = OutputFormat::from_str(&val).unwrap_or_default();
|
||||
@ -76,25 +135,12 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
||||
};
|
||||
|
||||
view! {
|
||||
<div class="cipher-card">
|
||||
<div class="card-header">
|
||||
<h2>{algorithm.to_string()}</h2>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>"Configuration"</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
|
||||
/>
|
||||
<RadioButton value=OperationMode::Encrypt current=mode set_current=set_mode />
|
||||
<RadioButton value=OperationMode::Decrypt current=mode set_current=set_mode />
|
||||
</div>
|
||||
{move || {
|
||||
if mode.get() != OperationMode::Decrypt {
|
||||
@ -124,6 +170,12 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn KeyInput(set_key_input: WriteSignal<String>) -> impl IntoView {
|
||||
view! {
|
||||
<div class="form-group">
|
||||
<label>"Secret Key"</label>
|
||||
<input
|
||||
@ -133,6 +185,15 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
||||
on:input=move |ev| set_key_input(event_target_value(&ev))
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn TextInput(
|
||||
mode: ReadSignal<OperationMode>,
|
||||
set_text_input: WriteSignal<String>,
|
||||
) -> impl IntoView {
|
||||
view! {
|
||||
<div class="form-group">
|
||||
<label>
|
||||
{move || {
|
||||
@ -149,12 +210,17 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
||||
on:input=move |ev| set_text_input(event_target_value(&ev))
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<button class="btn-primary" on:click=move |_| handle_submit()>
|
||||
{move || format!("{} using {algorithm}", mode.get())}
|
||||
</button>
|
||||
|
||||
// Output Section
|
||||
#[component]
|
||||
fn OutputBox(
|
||||
output: ReadSignal<String>,
|
||||
output_fmt: ReadSignal<OutputFormat>,
|
||||
copy_to_clipboard: impl Fn(String) + Copy + Send + 'static,
|
||||
copy_feedback: ReadSignal<bool>,
|
||||
) -> impl IntoView {
|
||||
view! {
|
||||
{move || {
|
||||
if output.get().is_empty() {
|
||||
return view! { <span></span> }.into_any();
|
||||
@ -163,10 +229,7 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
||||
<div class="result-box">
|
||||
<div class="result-toolbar">
|
||||
<strong>"Output ("{output_fmt.get().to_string()}")"</strong>
|
||||
<button
|
||||
class="btn-copy"
|
||||
on:click=move |_| copy_to_clipboard(output.get())
|
||||
>
|
||||
<button class="btn-copy" on:click=move |_| copy_to_clipboard(output.get())>
|
||||
{move || {
|
||||
if copy_feedback.get() { "✔️ Copied" } else { "📋 Copy" }
|
||||
}}
|
||||
@ -177,36 +240,17 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
||||
}
|
||||
.into_any()
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
// Error Section
|
||||
#[component]
|
||||
fn ErrorBox(error_msg: ReadSignal<String>) -> impl IntoView {
|
||||
view! {
|
||||
{move || {
|
||||
if error_msg.get().is_empty() {
|
||||
return view! { <span></span> }.into_any();
|
||||
}
|
||||
view! { <div class="error-box">{error_msg.get()}</div> }.into_any()
|
||||
}}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn RadioButton(
|
||||
value: OperationMode,
|
||||
current: ReadSignal<OperationMode>,
|
||||
set_current: WriteSignal<OperationMode>,
|
||||
) -> impl IntoView {
|
||||
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>
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user