重构优化

This commit is contained in:
tommy
2025-11-07 15:39:27 +08:00
parent f3ec5a56ff
commit 50ec5bd920
26 changed files with 74 additions and 225 deletions

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn AspectRatio( pub fn AspectRatio(
#[props(default = 1.0f32)] ratio: f32, #[props(default = 1.0f32)] ratio: f32,

View File

@@ -1,14 +1,6 @@
use chrono::{Datelike, Duration, NaiveDate}; use chrono::{Datelike, Duration, NaiveDate};
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
const WEEKDAY_LABELS: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; const WEEKDAY_LABELS: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
fn first_day_of_month(date: NaiveDate) -> NaiveDate { fn first_day_of_month(date: NaiveDate) -> NaiveDate {

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Card(#[props(into, default)] class: Option<String>, children: Element) -> Element { pub fn Card(#[props(into, default)] class: Option<String>, children: Element) -> Element {
let classes = merge_class("ui-card", class); let classes = merge_class("ui-card", class);

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Checkbox( pub fn Checkbox(
#[props(default)] checked: bool, #[props(default)] checked: bool,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[derive(Clone)] #[derive(Clone)]
struct CollapsibleContext { struct CollapsibleContext {
open: Signal<bool>, open: Signal<bool>,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct ComboboxOption { pub struct ComboboxOption {
pub label: String, pub label: String,

View File

@@ -1,18 +1,10 @@
use super::button::{Button, ButtonSize, ButtonVariant}; use super::button::{Button, ButtonSize, ButtonVariant};
use chrono::{Datelike, Duration, NaiveDate}; use chrono::{Datelike, Duration, NaiveDate};
use dioxus::prelude::*; use dioxus::prelude::*;
#[cfg(not(target_arch = "wasm32"))] use super::utils::merge_class;#[cfg(not(target_arch = "wasm32"))]
use std::time::SystemTime; use std::time::SystemTime;
use crate::components::ui::PopoverHandle; use crate::components::ui::PopoverHandle;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DateRange { pub struct DateRange {
pub start: NaiveDate, pub start: NaiveDate,

View File

@@ -1,15 +1,7 @@
use dioxus::html::events::{DragEvent, FormEvent}; use dioxus::html::events::{DragEvent, FormEvent};
use dioxus::html::{FileData, HasFileData}; use dioxus::html::{FileData, HasFileData};
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct FileMetadata { pub struct FileMetadata {
pub name: String, pub name: String,

View File

@@ -1,14 +1,6 @@
use crate::components::ui::Label; use crate::components::ui::Label;
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FormMessageVariant { pub enum FormMessageVariant {
Helper, Helper,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Input( pub fn Input(
#[props(into, default)] class: Option<String>, #[props(into, default)] class: Option<String>,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Label( pub fn Label(
#[props(into, default)] class: Option<String>, #[props(into, default)] class: Option<String>,

View File

@@ -2,6 +2,8 @@
//! Each component mirrors the styling and API conventions of the upstream React components while //! Each component mirrors the styling and API conventions of the upstream React components while
//! remaining idiomatic to Rust and Dioxus. //! remaining idiomatic to Rust and Dioxus.
mod utils;
mod accordion; mod accordion;
mod alert; mod alert;
mod aspect_ratio; mod aspect_ratio;

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Progress( pub fn Progress(
#[props(default = 0.0f32)] value: f32, #[props(default = 0.0f32)] value: f32,

View File

@@ -1,15 +1,7 @@
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
static RADIO_GROUP_IDS: AtomicUsize = AtomicUsize::new(0); static RADIO_GROUP_IDS: AtomicUsize = AtomicUsize::new(0);
fn next_radio_group_name() -> String { fn next_radio_group_name() -> String {

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn ScrollArea( pub fn ScrollArea(
#[props(into, default)] class: Option<String>, #[props(into, default)] class: Option<String>,

View File

@@ -1,5 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SeparatorOrientation { pub enum SeparatorOrientation {
Horizontal, Horizontal,
@@ -21,14 +21,6 @@ impl Default for SeparatorOrientation {
} }
} }
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Separator( pub fn Separator(
#[props(default)] orientation: SeparatorOrientation, #[props(default)] orientation: SeparatorOrientation,

View File

@@ -1,21 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::{merge_class, data_bool};
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
fn data_bool(value: bool) -> &'static str {
if value {
"true"
} else {
"false"
}
}
#[component] #[component]
pub fn Sidebar( pub fn Sidebar(
#[props(default)] collapsed: bool, #[props(default)] collapsed: bool,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Skeleton( pub fn Skeleton(
#[props(into, default)] class: Option<String>, #[props(into, default)] class: Option<String>,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Slider( pub fn Slider(
#[props(default = 0.0f32)] value: f32, #[props(default = 0.0f32)] value: f32,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Switch( pub fn Switch(
#[props(default)] checked: bool, #[props(default)] checked: bool,

View File

@@ -6,15 +6,7 @@ use std::{
use crate::components::ui::Checkbox; use crate::components::ui::Checkbox;
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Table(#[props(into, default)] class: Option<String>, children: Element) -> Element { pub fn Table(#[props(into, default)] class: Option<String>, children: Element) -> Element {
let classes = merge_class("ui-table", class); let classes = merge_class("ui-table", class);

View File

@@ -1,6 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TabsOrientation { pub enum TabsOrientation {
Horizontal, Horizontal,
@@ -28,14 +27,6 @@ struct TabsContext {
on_change: Option<EventHandler<String>>, on_change: Option<EventHandler<String>>,
} }
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Tabs( pub fn Tabs(
#[props(into)] default_value: String, #[props(into)] default_value: String,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Textarea( pub fn Textarea(
#[props(into, default)] class: Option<String>, #[props(into, default)] class: Option<String>,

View File

@@ -1,13 +1,5 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[component] #[component]
pub fn Toggle( pub fn Toggle(
#[props(default)] pressed: bool, #[props(default)] pressed: bool,

View File

@@ -1,14 +1,6 @@
use crate::components::ui::Toggle; use crate::components::ui::Toggle;
use dioxus::prelude::*; use dioxus::prelude::*;
use super::utils::merge_class;
fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ToggleGroupMode { pub enum ToggleGroupMode {
Single, Single,

View File

@@ -0,0 +1,48 @@
/// Merge a base CSS class with an optional extra class string.
/// If the extra string is empty or only whitespace, returns just the base class.
pub fn merge_class(base: &str, extra: Option<String>) -> String {
if let Some(extra) = extra.filter(|extra| !extra.trim().is_empty()) {
format!("{base} {}", extra.trim())
} else {
base.to_string()
}
}
/// Convert a boolean to "true" or "false" string for data attributes.
pub fn data_bool(value: bool) -> &'static str {
if value {
"true"
} else {
"false"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_merge_class_with_extra() {
assert_eq!(
merge_class("base", Some("extra".to_string())),
"base extra"
);
}
#[test]
fn test_merge_class_without_extra() {
assert_eq!(merge_class("base", None), "base");
}
#[test]
fn test_merge_class_with_empty_extra() {
assert_eq!(merge_class("base", Some("".to_string())), "base");
assert_eq!(merge_class("base", Some(" ".to_string())), "base");
}
#[test]
fn test_data_bool() {
assert_eq!(data_bool(true), "true");
assert_eq!(data_bool(false), "false");
}
}