use crate::core::traits::{Form, FormState};
use crate::error::FormError;
use leptos::prelude::{GetUntracked, Set};
use super::core::FormHandle;
impl<T: Form + Send + Sync + PartialEq> FormHandle<T> {
pub fn submit(&self) -> Result<T, FormError> {
self.validate()?;
let state = self.state.get_untracked();
let new_state = state.mark_submitting();
self.state.set(new_state.clone());
Ok(new_state.values)
}
pub fn reset(&self) {
let _state = self.state.get_untracked();
let initial_values = T::default_values();
let new_state = FormState::new(initial_values);
self.state.set(new_state);
}
pub fn reset_to(&self, form: T) {
let new_state = FormState::new(form);
self.state.set(new_state);
}
pub fn reset_values_keep_errors(&self) {
let state = self.state.get_untracked();
let initial_values = T::default_values();
let new_state = FormState {
values: initial_values,
is_dirty: false,
is_submitting: false,
errors: state.errors,
};
self.state.set(new_state);
}
pub fn reset_values_clear_errors(&self) {
let initial_values = T::default_values();
let new_state = FormState::new(initial_values);
self.state.set(new_state);
}
pub fn mark_submitting(&self) {
let state = self.state.get_untracked();
let new_state = state.mark_submitting();
self.state.set(new_state);
}
pub fn mark_not_submitting(&self) {
let state = self.state.get_untracked();
let new_state = state.mark_not_submitting();
self.state.set(new_state);
}
pub fn mark_dirty(&self) {
let state = self.state.get_untracked();
let new_state = state.mark_dirty();
self.state.set(new_state);
}
pub fn mark_clean(&self) {
let state = self.state.get_untracked();
let new_state = FormState {
values: state.values,
is_dirty: false,
is_submitting: state.is_submitting,
errors: state.errors,
};
self.state.set(new_state);
}
pub fn save_state(&self) -> FormState<T> {
self.state.get_untracked()
}
pub fn restore_state(&self, saved_state: FormState<T>) {
self.state.set(saved_state);
}
pub fn get_state_snapshot(&self) -> FormStateSnapshot<T> {
let state = self.state.get_untracked();
FormStateSnapshot {
values: state.values.clone(),
is_dirty: state.is_dirty,
is_submitting: state.is_submitting,
has_errors: !state.errors.is_empty(),
error_count: state.errors.error_count(),
}
}
pub fn restore_from_snapshot(&self, snapshot: FormStateSnapshot<T>) {
let current_state = self.state.get_untracked();
let new_state = FormState {
values: snapshot.values,
is_dirty: snapshot.is_dirty,
is_submitting: snapshot.is_submitting,
errors: if snapshot.has_errors {
current_state.errors
} else {
crate::validation::ValidationErrors::new()
},
};
self.state.set(new_state);
}
pub fn can_submit(&self) -> bool {
let state = self.state.get_untracked();
!state.is_submitting && state.is_valid()
}
pub fn can_reset(&self) -> bool {
let state = self.state.get_untracked();
state.is_dirty || !state.errors.is_empty()
}
pub fn get_submission_status(&self) -> SubmissionStatus {
let state = self.state.get_untracked();
if state.is_submitting {
SubmissionStatus::Submitting
} else if state.is_valid() {
SubmissionStatus::Ready
} else {
SubmissionStatus::HasErrors
}
}
pub fn submit_with_handler<F>(&self, handler: F) -> Result<T, FormError>
where
F: FnOnce(&T) -> Result<T, FormError>,
{
self.validate()?;
let state = self.state.get_untracked();
let new_state = state.mark_submitting();
self.state.set(new_state.clone());
match handler(&new_state.values) {
Ok(result) => {
let final_state = new_state.mark_not_submitting();
self.state.set(final_state);
Ok(result)
}
Err(error) => {
let final_state = new_state.mark_not_submitting();
self.state.set(final_state);
Err(error)
}
}
}
pub fn reset_with_confirmation(&self, confirm: bool) -> bool {
if confirm {
self.reset();
true
} else {
false
}
}
pub fn reset_with_callback<F>(&self, callback: F)
where
F: FnOnce(&T),
{
let initial_values = T::default_values();
callback(&initial_values);
self.reset_to(initial_values);
}
pub fn get_lifecycle_state(&self) -> FormLifecycleState {
let state = self.state.get_untracked();
FormLifecycleState {
is_dirty: state.is_dirty,
is_submitting: state.is_submitting,
has_errors: !state.errors.is_empty(),
is_valid: state.is_valid(),
can_submit: self.can_submit(),
can_reset: self.can_reset(),
}
}
}
#[derive(Debug, Clone)]
pub struct FormStateSnapshot<T: Form> {
pub values: T,
pub is_dirty: bool,
pub is_submitting: bool,
pub has_errors: bool,
pub error_count: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SubmissionStatus {
Ready,
Submitting,
HasErrors,
}
impl SubmissionStatus {
pub fn display_name(&self) -> &'static str {
match self {
SubmissionStatus::Ready => "Ready",
SubmissionStatus::Submitting => "Submitting",
SubmissionStatus::HasErrors => "Has Errors",
}
}
pub fn is_ready(&self) -> bool {
matches!(self, SubmissionStatus::Ready)
}
pub fn is_submitting(&self) -> bool {
matches!(self, SubmissionStatus::Submitting)
}
pub fn has_errors(&self) -> bool {
matches!(self, SubmissionStatus::HasErrors)
}
}
#[derive(Debug, Clone)]
pub struct FormLifecycleState {
pub is_dirty: bool,
pub is_submitting: bool,
pub has_errors: bool,
pub is_valid: bool,
pub can_submit: bool,
pub can_reset: bool,
}
impl FormLifecycleState {
pub fn summary(&self) -> String {
let mut parts = Vec::new();
if self.is_dirty {
parts.push("dirty");
}
if self.is_submitting {
parts.push("submitting");
}
if self.has_errors {
parts.push("has errors");
}
if self.is_valid {
parts.push("valid");
}
if parts.is_empty() {
"clean".to_string()
} else {
parts.join(", ")
}
}
pub fn is_clean(&self) -> bool {
!self.is_dirty && !self.has_errors && !self.is_submitting
}
pub fn needs_attention(&self) -> bool {
self.has_errors || self.is_dirty
}
}