refactor: extract form atoms (FormInput, FormLabel, AlertBox) and replace repeated input/button styles
This commit is contained in:
parent
71abff1b3e
commit
4e33bd3ee6
54
src/components/forms.rs
Normal file
54
src/components/forms.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
pub const INPUT_CLASS: &str = "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600";
|
||||||
|
|
||||||
|
pub const BUTTON_PRIMARY_CLASS: &str = "w-full py-2 px-4 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 font-medium rounded-full hover:opacity-80 transition-opacity cursor-pointer";
|
||||||
|
|
||||||
|
pub const BUTTON_SECONDARY_CLASS: &str = "px-6 py-2 bg-gray-200 dark:bg-[#333] text-gray-700 dark:text-[#dadadb] rounded-full font-medium hover:opacity-80 transition-opacity cursor-pointer";
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn FormInput(
|
||||||
|
r#type: &'static str,
|
||||||
|
placeholder: &'static str,
|
||||||
|
value: String,
|
||||||
|
oninput: EventHandler<String>,
|
||||||
|
onkeydown: Option<EventHandler<KeyboardEvent>>,
|
||||||
|
) -> Element {
|
||||||
|
rsx! {
|
||||||
|
input {
|
||||||
|
class: "{INPUT_CLASS}",
|
||||||
|
r#type: "{r#type}",
|
||||||
|
placeholder: "{placeholder}",
|
||||||
|
value: "{value}",
|
||||||
|
oninput: move |e| oninput.call(e.value()),
|
||||||
|
onkeydown: move |e| {
|
||||||
|
if let Some(ref handler) = onkeydown {
|
||||||
|
handler.call(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn FormLabel(label: &'static str) -> Element {
|
||||||
|
rsx! {
|
||||||
|
label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1",
|
||||||
|
"{label}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn AlertBox(message: String, variant: &'static str) -> Element {
|
||||||
|
let (bg_class, text_class) = match variant {
|
||||||
|
"error" => ("bg-red-100 dark:bg-red-900/30", "text-red-700 dark:text-red-300"),
|
||||||
|
"success" => ("bg-green-100 dark:bg-green-900/30", "text-green-700 dark:text-green-300"),
|
||||||
|
_ => ("bg-gray-100 dark:bg-[#333]", "text-gray-700 dark:text-[#9b9c9d]"),
|
||||||
|
};
|
||||||
|
rsx! {
|
||||||
|
div { class: "mb-4 p-3 {bg_class} {text_class} rounded-lg text-center",
|
||||||
|
"{message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
pub mod admin_layout;
|
pub mod admin_layout;
|
||||||
pub mod admin_skeleton;
|
pub mod admin_skeleton;
|
||||||
pub mod footer;
|
pub mod footer;
|
||||||
|
pub mod forms;
|
||||||
pub mod frontend_layout;
|
pub mod frontend_layout;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod nav;
|
pub mod nav;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use dioxus::prelude::*;
|
|||||||
use dioxus::router::components::Link;
|
use dioxus::router::components::Link;
|
||||||
|
|
||||||
use crate::api::auth::{login, AuthResponse};
|
use crate::api::auth::{login, AuthResponse};
|
||||||
|
use crate::components::forms::{AlertBox, FormInput, FormLabel, BUTTON_PRIMARY_CLASS};
|
||||||
use crate::router::Route;
|
use crate::router::Route;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -54,40 +55,32 @@ pub fn Login() -> Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(err) = error() {
|
if let Some(err) = error() {
|
||||||
div { class: "mb-4 p-3 bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 rounded-lg text-center",
|
AlertBox { message: err, variant: "error" }
|
||||||
"{err}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div { class: "space-y-4",
|
div { class: "space-y-4",
|
||||||
div {
|
div {
|
||||||
label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1",
|
FormLabel { label: "用户名 / 邮箱" }
|
||||||
"用户名 / 邮箱"
|
FormInput {
|
||||||
}
|
|
||||||
input {
|
|
||||||
class: "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
|
||||||
r#type: "text",
|
r#type: "text",
|
||||||
placeholder: "用户名或邮箱",
|
placeholder: "用户名或邮箱",
|
||||||
value: username(),
|
value: username(),
|
||||||
oninput: move |e| username.set(e.value()),
|
oninput: move |v: String| username.set(v),
|
||||||
onkeydown: move |e| if e.key() == Key::Enter { on_submit(()) },
|
onkeydown: Some(EventHandler::new(move |e: KeyboardEvent| if e.key() == Key::Enter { on_submit(()) })),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1",
|
FormLabel { label: "密码" }
|
||||||
"密码"
|
FormInput {
|
||||||
}
|
|
||||||
input {
|
|
||||||
class: "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
|
||||||
r#type: "password",
|
r#type: "password",
|
||||||
placeholder: "密码",
|
placeholder: "密码",
|
||||||
value: password(),
|
value: password(),
|
||||||
oninput: move |e| password.set(e.value()),
|
oninput: move |v: String| password.set(v),
|
||||||
onkeydown: move |e| if e.key() == Key::Enter { on_submit(()) },
|
onkeydown: Some(EventHandler::new(move |e: KeyboardEvent| if e.key() == Key::Enter { on_submit(()) })),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
class: "w-full py-2 px-4 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 font-medium rounded-full hover:opacity-80 transition-opacity",
|
class: "{BUTTON_PRIMARY_CLASS}",
|
||||||
onclick: move |_| on_submit(()),
|
onclick: move |_| on_submit(()),
|
||||||
"登录"
|
"登录"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use dioxus::prelude::*;
|
|||||||
use dioxus::router::components::Link;
|
use dioxus::router::components::Link;
|
||||||
|
|
||||||
use crate::api::auth::{register, AuthResponse};
|
use crate::api::auth::{register, AuthResponse};
|
||||||
|
use crate::components::forms::{AlertBox, FormInput, FormLabel, BUTTON_PRIMARY_CLASS};
|
||||||
use crate::router::Route;
|
use crate::router::Route;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -60,72 +61,63 @@ pub fn Register() -> Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if success() {
|
if success() {
|
||||||
div { class: "mb-4 p-3 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 rounded-lg text-center",
|
AlertBox {
|
||||||
"注册成功!"
|
message: "注册成功!".to_string(),
|
||||||
Link { class: "block mt-2 text-gray-700 dark:text-[#dadadb] hover:underline cursor-pointer",
|
variant: "success",
|
||||||
to: Route::Login {},
|
}
|
||||||
"去登录"
|
Link { class: "block text-center mt-2 text-gray-700 dark:text-[#dadadb] hover:underline cursor-pointer",
|
||||||
}
|
to: Route::Login {},
|
||||||
|
"去登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(err) = error() {
|
if let Some(err) = error() {
|
||||||
div { class: "mb-4 p-3 bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 rounded-lg text-center",
|
AlertBox { message: err, variant: "error" }
|
||||||
"{err}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div { class: "space-y-4",
|
div { class: "space-y-4",
|
||||||
div {
|
div {
|
||||||
label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1",
|
FormLabel { label: "用户名" }
|
||||||
"用户名"
|
FormInput {
|
||||||
}
|
|
||||||
input {
|
|
||||||
class: "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
|
||||||
r#type: "text",
|
r#type: "text",
|
||||||
placeholder: "3-50 位字符",
|
placeholder: "3-50 位字符",
|
||||||
value: username(),
|
value: username(),
|
||||||
oninput: move |e| username.set(e.value()),
|
oninput: move |v: String| username.set(v),
|
||||||
|
onkeydown: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1",
|
FormLabel { label: "邮箱" }
|
||||||
"邮箱"
|
FormInput {
|
||||||
}
|
|
||||||
input {
|
|
||||||
class: "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
|
||||||
r#type: "email",
|
r#type: "email",
|
||||||
placeholder: "your@email.com",
|
placeholder: "your@email.com",
|
||||||
value: email(),
|
value: email(),
|
||||||
oninput: move |e| email.set(e.value()),
|
oninput: move |v: String| email.set(v),
|
||||||
|
onkeydown: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1",
|
FormLabel { label: "密码" }
|
||||||
"密码"
|
FormInput {
|
||||||
}
|
|
||||||
input {
|
|
||||||
class: "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
|
||||||
r#type: "password",
|
r#type: "password",
|
||||||
placeholder: "至少 8 位",
|
placeholder: "至少 8 位",
|
||||||
value: password(),
|
value: password(),
|
||||||
oninput: move |e| password.set(e.value()),
|
oninput: move |v: String| password.set(v),
|
||||||
|
onkeydown: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1",
|
FormLabel { label: "确认密码" }
|
||||||
"确认密码"
|
FormInput {
|
||||||
}
|
|
||||||
input {
|
|
||||||
class: "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600",
|
|
||||||
r#type: "password",
|
r#type: "password",
|
||||||
placeholder: "再次输入密码",
|
placeholder: "再次输入密码",
|
||||||
value: confirm_password(),
|
value: confirm_password(),
|
||||||
oninput: move |e| confirm_password.set(e.value()),
|
oninput: move |v: String| confirm_password.set(v),
|
||||||
|
onkeydown: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
class: "w-full py-2 px-4 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 font-medium rounded-full hover:opacity-80 transition-opacity",
|
class: "{BUTTON_PRIMARY_CLASS}",
|
||||||
onclick: on_submit,
|
onclick: on_submit,
|
||||||
"注册"
|
"注册"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user